数据结构好题!
因为思路是第一次见,所以就直接说思路。
题目抽象:
这是一个矩形
里面有很多的点,求至少覆盖k个点的矩形有多少个
先确定上边界,下边界为低端
上边界下面的点用链表存起来
考虑以每个点作为左边界的贡献(线上的点也算在矩形内),假如k=3,那么右边界至少在橙色这根线这儿
符合要求的矩形的左边界范围L和右边界范围R如图
做出的贡献为L*R,如果前面还有点注意是左开右闭(因为右边到底都是可以的)
然后移动左边界
到下一个点再计算贡献
再来移动下边界
一些点可能在同一水平线上,所以一开始可以用vector存起来,然后要删的点一个一个处理。现在考虑下边界移上去后删去一些点的影响
比如删去这个点
(k=3),对于前面k-1个点和后面一个点会造成影响:前面k-1个点:删去后因为每次至少覆盖k个点,所以右边界要向右移动一个点;后面一个点:因为删去了前面的那个点,所以左边界要向左移动
所以总的思路就是:枚举上边界,确定左边界和右边界,再移动下边界。
时间复杂度O(n^2 k)。上边界往下,下边界走到上边界,上边界n条,相当于图从上往下用线扫n次,每次链表中最多n个点,那么左边界最多有n条,每一条往后推k个点,n*n*k=n^2*k。
实现的细节在代码里有说,记得注意左开右闭以及删点影响的范围(画图看一下就明白了)。
#include<bits/stdc++.h> #define N 3021 #define LL long long using namespace std; int read() { int x=0,f=1;char s=getchar(); while(s<‘0‘||s>‘9‘){if(s==‘-‘)f=-1;s=getchar();} while(s>=‘0‘&&s<=‘9‘){x=x*10+s-‘0‘;s=getchar();} return x*f; } vector<int>belong[N]; struct point{ int x,y; }a[N*N]; LL val[N],id[N],pointx[N],pointy[N],nextt[N],pre[N],ord[N]; LL sum=0,ans=0,cnt=0,k,r,c,n; bool cmp(const int &u,const int &v) { if(pointy[u]==pointy[v])return pointx[u]<pointx[v]; return pointy[u]<pointy[v]; } void del(int d) { nextt[pre[d]]=nextt[d]; pre[nextt[d]]=pre[d]; sum-=val[d];//以d为左边界的矩形要减去 int x=nextt[d],y=nextt[d];//对前k的点和后一个点作为左边界会有影响 for(int i=1;i<k;++i)y=nextt[y]; for(int i=1;i<=k;++i) { int v=(pointy[x]-pointy[pre[x]])*(c-pointy[y]+1);//如果说两点在同一竖直线上则v=0,也满足要求 sum+=v-val[x]; val[x]=v; x=pre[x];y=pre[y]; } } int idc; int main() { freopen("baritone.in","r",stdin); freopen("baritone.out","w",stdout); r=read(),c=read(),n=read();k=read(); for(int i=1;i<=n;++i) a[i].x=read(),a[i].y=read(),belong[a[i].x].push_back(i);////保存这一行有那些点 for(int i=1;i<=r;++i)//枚举上边界 { sum=0,cnt=0;//sum是全局变量,每一层下边界改变时会变化,每一个上边界对应一个sum for(int j=0;j<=10;++j)//防止越界,k最大是10 pointy[++cnt]=0,pointy[++cnt]=c+1; for(int j=1;j<=n;++j) if(a[j].x>=i)//pointx 行坐标,pointy 纵坐标 pointy[++cnt]=a[j].y,pointx[cnt]=a[j].x,ord[j]=cnt;//ord[j]是第j个点在链表中的序号 for(int j=1;j<=cnt;++j)id[j]=j; sort(id+1,id+1+cnt,cmp); for(int j=1;j<cnt;++j)//前驱后继 nextt[id[j]]=id[j+1],pre[id[j+1]]=id[j]; nextt[id[cnt]]=id[cnt];pre[id[1]]=id[1]; for(int j=1;j<=cnt;++j)//枚举左边界 { int R=j; for(int p=1;p<k;++p) R=nextt[R];//R为右边界 val[j]=(pointy[j]-pointy[pre[j]])*(c-pointy[R]+1);//乘法原理 ,右边界一直到c都是满足的 sum+=val[j];//val上边界为i,下边界为r,左边界为j的矩形个数 } for(int j=r;j>=i;--j)//枚举下边界 { ans+=sum; for(int p=0;p<(int)belong[j].size();++p) del(ord[belong[j][p]]); } } printf("%lld\n",ans); } /* 2 2 1 1 1 2 3 2 3 3 1 1 3 1 2 2 3 2 3 2 1 1 3 1 2 2 */
这道题还是很有价值的!??ヽ(°▽°)ノ?
原文地址:https://www.cnblogs.com/yyys-/p/11405741.html
时间: 2024-10-03 22:55:59