题解:直接在输入点对的基础上建立 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;
}