【洛谷P1429】平面最近点对

题解:直接在输入点对的基础上建立 kd-tree,再每次以每个节点的坐标查询离这个点最近的点即可,同时需要忽略这个点本身对该点答案的贡献。

另外,直接在这些点上建立 kd-tree 会比一个一个插入点建立的更平衡,直接插入由于缺少了 nth_element 的划分,导致树很容易退化。

代码如下

#include <bits/stdc++.h>
using namespace std;
const int maxn=2e5+10;
inline double sqr(double val){return val*val;}

struct node{
	#define ls(x) t[x].lc
	#define rs(x) t[x].rc
	int lc,rc;
	double p[2],x[2],y[2];
}t[maxn];
int root,d;double ans;
inline bool cmp(const node &x,const node &y){return x.p[d]<y.p[d];}
inline double getdis(int o,double x,double y){
	return sqr(max((double)0,max(t[o].x[0]-x,x-t[o].x[1])))+sqr(max((double)0,max(t[o].y[0]-y,y-t[o].y[1])));
}
inline void pushup(int o){
	t[o].x[0]=min(t[o].p[0],min(t[ls(o)].x[0],t[rs(o)].x[0]));
	t[o].x[1]=max(t[o].p[0],max(t[ls(o)].x[1],t[rs(o)].x[1]));
	t[o].y[0]=min(t[o].p[1],min(t[ls(o)].y[0],t[rs(o)].y[0]));
	t[o].y[1]=max(t[o].p[1],max(t[ls(o)].y[1],t[rs(o)].y[1]));
}
int build(int l,int r,int now){
	if(l>r)return 0;
	int mid=l+r>>1;
	d=now,nth_element(t+l,t+mid,t+r+1,cmp);
	ls(mid)=build(l,mid-1,now^1),rs(mid)=build(mid+1,r,now^1);
	return pushup(mid),mid;
}
void query(int o,double x,double y,int idx){
	double dn=sqr(x-t[o].p[0])+sqr(y-t[o].p[1]),dl,dr;
	if(o!=idx)ans=min(ans,dn);
	dl=ls(o)?getdis(ls(o),x,y):4e18;
	dr=rs(o)?getdis(rs(o),x,y):4e18;
	if(dl<dr){
		if(dl<ans)query(ls(o),x,y,idx);
		if(dr<ans)query(rs(o),x,y,idx);
	}else{
		if(dr<ans)query(rs(o),x,y,idx);
		if(dl<ans)query(ls(o),x,y,idx);
	}
}
void init(){ans=4e18,t[0].x[0]=t[0].y[0]=4e18,t[0].x[1]=t[0].y[1]=-4e18;}

int n;double p[2];
int main(){
	init();
	scanf("%d",&n);
	for(int i=1;i<=n;i++)scanf("%lf%lfd",&t[i].p[0],&t[i].p[1]);
	root=build(1,n,0);
	for(int i=1;i<=n;i++)query(root,t[i].p[0],t[i].p[1],i);
	printf("%.4lf
",sqrt(ans));
	return 0;
} 
原文地址:https://www.cnblogs.com/wzj-xhjbk/p/10550619.html