題解 P6247 【[SDOI2012]最近最遠點對】
阿新 • • 發佈:2020-12-13
\(\text{K-D Tree}\) 很簡單。 ——\(\texttt{zwj}\)
Solution
一種方法是先把整棵K-D Tree建好,然後再詢問每個點的最近最遠點(注意剪枝),雖然可以優化建樹使空間切割更平均,但是這樣每對點會計算兩次,常數*2。
還可以動態插點,對於每個點,先詢問當前K-D Tree中的最近最遠點,再插入,就能保證每對點只算一次。
不過可能出題人會卡你這種做法,我們可以玄學優化:隨機化插點順序,效果十分明顯。
Code
#include<bits/stdc++.h> using namespace std; const int maxn=1e5+5; int n,now,ls[maxn],rs[maxn],rt; double mxx[maxn],mxy[maxn],mnx[maxn],mny[maxn],ans1=9e18,ans2; struct node{ double x,y; }a[maxn]; void pushup(int u){ mxx[u]=max(mxx[u],a[now].x);mxy[u]=max(mxy[u],a[now].y); mnx[u]=min(mnx[u],a[now].x);mny[u]=min(mny[u],a[now].y); } double sqr(double x){ return x*x; } double dis(int u){ return sqr(a[now].x-a[u].x)+sqr(a[now].y-a[u].y); } double mindis(int u){//子樹u中最小距離 return sqr(max(a[now].x-mxx[u],0.0)+max(mnx[u]-a[now].x,0.0))+sqr(max(a[now].y-mxy[u],0.0)+max(mny[u]-a[now].y,0.0)); } double maxdis(int u){//子樹u中最小距離 return max(sqr(a[now].x-mxx[u]),sqr(mnx[u]-a[now].x))+max(sqr(a[now].y-mxy[u]),sqr(mny[u]-a[now].y)); } void ins(int &u,bool op){ if(!u)return u=now,void(); if(!op)ins(a[now].x<=a[u].x?ls[u]:rs[u],1); else ins(a[now].y<=a[u].y?ls[u]:rs[u],0); pushup(u); } void askmin(int u){ if(!u)return; if(u!=now)ans1=min(ans1,dis(u)); double l=mindis(ls[u]),r=mindis(rs[u]); if(l<r){//剪枝:如果比答案大就不搜了 if(l<ans1)askmin(ls[u]); if(r<ans1)askmin(rs[u]); }else{ if(r<ans1)askmin(rs[u]); if(l<ans1)askmin(ls[u]); } } void askmax(int u){ if(!u)return; ans2=max(ans2,dis(u)); double l=maxdis(ls[u]),r=maxdis(rs[u]); if(l>r){ if(l>ans2)askmax(ls[u]); if(r>ans2)askmax(rs[u]); }else{ if(r>ans2)askmax(rs[u]); if(l>ans2)askmax(ls[u]); } } int main(){ srand(20201213);//今天 scanf("%d",&n); for(int i=1;i<=n;i++) scanf("%lf%lf",&a[i].x,&a[i].y); random_shuffle(a+1,a+n+1); for(int i=1;i<=n;i++){ mxx[i]=mnx[i]=a[i].x; mxy[i]=mny[i]=a[i].y; now++;askmin(rt);askmax(rt);ins(rt,0); }printf("%.2lf %.2lf\n",sqrt(ans1),sqrt(ans2)); return 0; }