BZOJ5465 : [APIO 2018] 选圆圈
假设最大的圆半径为$R$,以$2R$为大小将地图划分为一个个格子,那么每个圆只需要检查圆心在附近$9$个格子内部的所有圆。
在当前圆的半径不足$\frac{R}{2}$时重构网格,那么最多重构$O(\log R)$次,且每个圆最多被检查常数次。
时间复杂度$O(n\log n\log R)$,利用Hash可以做到$O(n\log R)$。
#include<cstdio> #include<algorithm> #include<vector> using namespace std; const int N=300010,inf=1000000000,BUF=13000000; int n,m,i,x,y,q[N],ans[N],size;char Buf[BUF],*buf=Buf; struct E{int x,y,r;E(){}E(int _x,int _y,int _r){x=_x,y=_y,r=_r;}}e[N],f[N]; vector<int>g[N],tmp; inline void read(int&a){int f=0;for(a=0;*buf<45;buf++);if(*buf==45)f=1,buf++;while(*buf>47)a=a*10+*buf++-48;if(f)a=-a;} inline bool cmp(int x,int y){ if(e[x].r!=e[y].r)return e[x].r>e[y].r; return x<y; } inline bool cmpe(const E&a,const E&b){ if(a.x!=b.x)return a.x<b.x; return a.y<b.y; } inline void init(int _){ if(size&&size/2<_)return; size=_; int i,j,k,cnt; m=0; for(i=1;i<=n;i++)if(!ans[i]){ int x=e[i].x/size,y=e[i].y/size; f[++m]=E(x,y,i); } sort(f+1,f+m+1,cmpe); cnt=0; for(i=1;i<=m;i=j){ for(j=i;j<=m&&f[i].x==f[j].x&&f[i].y==f[j].y;j++); f[++cnt]=f[i]; g[cnt].clear(); for(k=i;k<j;k++)g[cnt].push_back(f[k].r); } m=cnt; } inline int ask(int x,int y){ int l=1,r=m,mid; while(l<=r){ mid=(l+r)>>1; if(f[mid].x==x&&f[mid].y==y)return mid; if(f[mid].x<x||f[mid].x==x&&f[mid].y<y)l=mid+1;else r=mid-1; } return 0; } inline bool check(int i,int j){return 1LL*(e[i].r+e[j].r)*(e[i].r+e[j].r)>=1LL*(e[i].x-e[j].x)*(e[i].x-e[j].x)+1LL*(e[i].y-e[j].y)*(e[i].y-e[j].y);} inline void apply(int S){ if(ans[S])return; init(e[S].r*2); int x=e[S].x/size,y=e[S].y/size,i,j,k,o,t; for(i=x-1;i<=x+1;i++)for(j=y-1;j<=y+1;j++)if(i>=0&&j>=0){ o=ask(i,j); if(!o||!g[o].size())continue; tmp.clear(); for(k=0;k<g[o].size();k++){ t=g[o][k]; if(check(S,t))ans[t]=S;else tmp.push_back(t); } swap(g[o],tmp); } } int main(){ fread(Buf,1,BUF,stdin);read(n); for(i=1;i<=n;i++){ read(x),read(y),read(e[i].r); e[i].x=x+inf; e[i].y=y+inf; q[i]=i; } sort(q+1,q+n+1,cmp); for(i=1;i<=n;i++)apply(q[i]); for(i=1;i<=n;i++)printf("%d%c",ans[i],i<n?' ':'\n'); return 0; }