题意:
平面上n个点,求一个点使得离它最近的点和最远的点离它的曼哈顿距离差最小(若选的点处已有点,则改点不算)。n≤500000
题解:
第一次写kd树,感觉眼睛又瞎了(玄学复杂度)。首先先把所有点横坐标和纵坐标轮流为关键字排序建一个平衡树,维护每棵子树里的横纵坐标最大最小值。在树上查询时就启发式搜索,比如说正在查询里某点最远的点,则如果当前子树的横纵坐标最大最小值与某点的曼哈顿距离小于答案,就返回,否则对左右子树估价,那边越可能得到最优解就先往哪边走。这么暴力的操作,却可以证明,单次查询均摊复杂度为O(sqrt(n))。好神奇(不过常数应该不小)。
对应于本题,先将所有点建成kd树,然后有奇怪性质:所求点一定是这些点中的一个,所以枚举所有点,查询离这个点最远最近的点的与它的曼哈顿距离差,寻找最小的一个输出。估价函数具体见代码,注意求最远点和求最近点的估价函数不一样。
代码:
1 #include2 #include 3 #include 4 #define inc(i,j,k) for(int i=j;i<=k;i++) 5 #define maxn 500010 6 #define INF 0x3fffffff 7 using namespace std; 8 9 inline int read(){10 char ch=getchar(); int f=1,x=0;11 while(ch<'0'||ch>'9'){ if(ch=='-')f=-1; ch=getchar();}12 while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();13 return f*x;14 }15 int n,f,rt,mxans,mnans;16 struct p{ int pos[2]; bool operator < (const p &a)const{ return pos[f] >1; nth_element(ps+l,ps+mid,ps+r+1);31 inc(i,0,1)nds[mid].mx[i]=nds[mid].mn[i]=ps[mid].pos[i]; nds[mid].pos=ps[mid];32 if(l dr){45 if(mxans dl)querymin(nds[x].lc,a); if(mnans>dr)querymin(nds[x].rc,a);55 }else{56 if(mnans>dr)querymin(nds[x].rc,a); if(mnans>dl)querymin(nds[x].lc,a);57 }58 }59 int query(p a){60 mxans=0; querymax(rt,a); mnans=INF; querymin(rt,a); return mxans-mnans;61 }62 int main(){63 n=read(); inc(i,1,n)ps[i].pos[0]=read(),ps[i].pos[1]=read(); rt=build(1,n,0);64 int ans=INF; inc(i,1,n)ans=min(ans,query(ps[i])); printf("%d",ans); return 0;65 }
20160906