在竞赛中,kd-tree一般只用于平面,很少有高于二维的情况。
在随机情况下,kd-tree的复杂度为O(NlogN),但会被极端数据卡到平方级别。
总而言之,就是优美的暴力。
查询时,通过估价函数进行减值。当然,这个函数一定要大于等于最后的结果,才有正确性。
1.求平面最近点对,欧几里得距离。精确到小数点后4位。
模板,不解释。
1 // luogu-judger-enable-o2 2 // luogu-judger-enable-o2 3 #include<bits/stdc++.h> 4 using namespace std; 5 const double eps=0.00001; 6 const double inf=1E18; 7 const int maxn=2E5+5; 8 int n,m; 9 int son[maxn][2],size; 10 double val[maxn][2],minv[maxn][2],maxv[maxn][2],ans,v[2]; 11 struct pt{double v[2];}a[maxn]; 12 bool cmp0(pt x,pt y){return x.v[0]<y.v[0];} 13 bool cmp1(pt x,pt y){return x.v[1]<y.v[1];} 14 bool cmp2(pt x,pt y) 15 { 16 if(x.v[0]==y.v[0])return x.v[1]<y.v[1]; 17 return x.v[0]<y.v[0]; 18 } 19 inline void update(int x,int y) 20 { 21 for(int i=0;i<2;++i) 22 { 23 minv[x][i]=min(minv[x][i],minv[y][i]); 24 maxv[x][i]=max(maxv[x][i],maxv[y][i]); 25 } 26 } 27 void build(int l,int r,int dep,int num) 28 { 29 int mid=(l+r)>>1; 30 if(dep%2==1)nth_element(a+l,a+mid,a+r+1,cmp0); 31 else nth_element(a+l,a+mid,a+r+1,cmp1); 32 minv[num][0]=maxv[num][0]=val[num][0]=a[mid].v[0]; 33 minv[num][1]=maxv[num][1]=val[num][1]=a[mid].v[1]; 34 if(l<=mid-1) 35 { 36 build(l,mid-1,dep+1,son[num][0]=++size); 37 update(num,son[num][0]); 38 } 39 if(mid+1<=r) 40 { 41 build(mid+1,r,dep+1,son[num][1]=++size); 42 update(num,son[num][1]); 43 } 44 } 45 inline double s(double x){return x*x;} 46 inline double dis(int x,double v[2]){return s(val[x][0]-v[0])+s(val[x][1]-v[1]);} 47 inline double f(int x,double v[2]) 48 { 49 double sum=0; 50 for(int i=0;i<2;++i) 51 { 52 if(minv[x][i]>v[i])sum+=s(minv[x][i]-v[i]); 53 if(maxv[x][i]<v[i])sum+=s(maxv[x][i]-v[i]); 54 } 55 return sum; 56 } 57 void ask(double v[2],double&ans,int num) 58 { 59 if(!num)return; 60 double d=dis(num,v); 61 if(d>=eps)ans=min(ans,d); 62 double lf=f(son[num][0],v),rf=f(son[num][1],v); 63 if(lf<rf) 64 { 65 if(lf<ans)ask(v,ans,son[num][0]); 66 if(rf<ans)ask(v,ans,son[num][1]); 67 } 68 else 69 { 70 if(rf<ans)ask(v,ans,son[num][1]); 71 if(lf<ans)ask(v,ans,son[num][0]); 72 } 73 } 74 int main() 75 { 76 ios::sync_with_stdio(false); 77 cin>>n; 78 for(int i=1;i<=n;++i)cin>>a[i].v[0]>>a[i].v[1]; 79 build(1,n,1,size=1); 80 sort(a+1,a+n+1,cmp2); 81 for(int i=1;i<n;++i) 82 { 83 if(a[i].v[0]==a[i+1].v[0]&&a[i].v[1]==a[i+1].v[1]) 84 { 85 cout<<"0.0000"<<endl; 86 return 0; 87 } 88 } 89 ans=inf; 90 for(int i=1;i<=n;++i) 91 { 92 v[0]=a[i].v[0]; 93 v[1]=a[i].v[1]; 94 ask(v,ans,1); 95 } 96 cout<<fixed<<setprecision(4)<<sqrt(ans)<<endl; 97 return 0; 98 }
https://www.luogu.org/problemnew/show/P1429
2.求平面最近点对,曼哈顿距离。
1 #pragma GCC optimize(2) 2 #include<bits/stdc++.h> 3 using namespace std; 4 const int maxn=1E6+5; 5 const int inf=INT_MAX; 6 int n,m,opt,ans; 7 int son[maxn][2],val[maxn][2],size,maxv[maxn][2],minv[maxn][2],v[2]; 8 struct pt{int v[2];}a[maxn]; 9 bool cmp0(pt x,pt y){return x.v[0]<y.v[0];} 10 bool cmp1(pt x,pt y){return x.v[1]<y.v[1];} 11 inline int read() 12 { 13 int w=0,q=0; 14 char c=getchar(); 15 while((c<'0' || c>'9') && c!='-') c=getchar(); 16 if (c=='-') q=1, c=getchar(); 17 while (c>='0' && c<='9') w=w*10+c-'0', c=getchar(); 18 return q ? -w : w; 19 } 20 void write(int x) 21 { 22 if(x<=9){putchar('0'+x);return;} 23 write(x/10); 24 putchar('0'+x%10); 25 } 26 inline void update(int x,int y) 27 { 28 for(int i=0;i<2;++i) 29 { 30 minv[x][i]=min(minv[x][i],minv[y][i]); 31 maxv[x][i]=max(maxv[x][i],maxv[y][i]); 32 } 33 } 34 void build(int l,int r,int dep,int num) 35 { 36 int mid=(l+r)>>1; 37 if(dep&1)nth_element(a+l,a+mid,a+r+1,cmp1); 38 else nth_element(a+l,a+mid,a+r+1,cmp0); 39 maxv[num][0]=minv[num][0]=val[num][0]=a[mid].v[0]; 40 maxv[num][1]=minv[num][1]=val[num][1]=a[mid].v[1]; 41 if(l<=mid-1) 42 { 43 build(l,mid-1,dep+1,son[num][0]=++size); 44 update(num,son[num][0]); 45 } 46 if(mid+1<=r) 47 { 48 build(mid+1,r,dep+1,son[num][1]=++size); 49 update(num,son[num][1]); 50 } 51 } 52 void insert(int dep,int num) 53 { 54 if(v[dep&1]<=val[num][dep&1]) 55 { 56 if(son[num][0])insert(dep+1,son[num][0]); 57 else 58 { 59 son[num][0]=++size; 60 maxv[size][0]=minv[size][0]=val[size][0]=v[0]; 61 maxv[size][1]=minv[size][1]=val[size][1]=v[1]; 62 } 63 update(num,son[num][0]); 64 } 65 else 66 { 67 if(son[num][1])insert(dep+1,son[num][1]); 68 else 69 { 70 son[num][1]=++size; 71 maxv[size][0]=minv[size][0]=val[size][0]=v[0]; 72 maxv[size][1]=minv[size][1]=val[size][1]=v[1]; 73 } 74 update(num,son[num][1]); 75 } 76 } 77 inline int f(int x) 78 { 79 int sum=0; 80 for(int i=0;i<2;++i) 81 { 82 if(minv[x][i]>v[i])sum+=minv[x][i]-v[i]; 83 if(maxv[x][i]<v[i])sum+=v[i]-maxv[x][i]; 84 } 85 return sum; 86 } 87 inline int dis(int x){return abs(val[x][0]-v[0])+abs(val[x][1]-v[1]);} 88 void ask(int&ans,int num) 89 { 90 if(num==0)return; 91 ans=min(ans,dis(num)); 92 int lf=f(son[num][0]),rf=f(son[num][1]); 93 if(lf<rf) 94 { 95 if(lf<ans)ask(ans,son[num][0]); 96 if(rf<ans)ask(ans,son[num][1]); 97 } 98 else 99 { 100 if(rf<ans)ask(ans,son[num][1]); 101 if(lf<ans)ask(ans,son[num][0]); 102 } 103 } 104 void out(int num,int dep) 105 { 106 if(num==0)return; 107 cout.width(6*dep); 108 cout<<val[num][0]<<" "<<val[num][1]<<endl; 109 out(son[num][0],dep+1); 110 out(son[num][1],dep+1); 111 } 112 int main() 113 { 114 ios::sync_with_stdio(false); 115 n=read();m=read(); 116 for(int i=1;i<=n;++i)a[i].v[0]=read(),a[i].v[1]=read(); 117 build(1,n,1,size=1); 118 while(m--) 119 { 120 opt=read(),v[0]=read(),v[1]=read(); 121 if(opt==1)insert(1,1); 122 else 123 { 124 ans=inf; 125 ask(ans,1); 126 write(ans); 127 putchar('\n'); 128 } 129 // out(1,0); 130 } 131 return 0; 132 }
https://www.lydsy.com/JudgeOnline/problem.php?id=2648
洛咕上T了。
3.求平面最远点对。精确到小数点后4位。
换个估价函数就行了,没代码。
4.求平面上k远点对。给出距离的平方,n≤100,000,k≤100。
博主的做法类似于超级钢琴和异或粽子的做法,先将所有的点的最远距离加入大根堆,每次取出最大元素,更新其次大的答案。
在查询过程中,为了防止访问到之前的答案,hash一下。
不要偷懒用map。1.6s--->16s。
1 #include<bits/stdc++.h> 2 #define mod 10000007 3 #define p 13131 4 using namespace std; 5 typedef long long int ll; 6 const ll maxn=1E5+5; 7 const ll inf=LONG_LONG_MAX; 8 9 ll min(ll x,ll y){return x<y?x:y;} 10 ll max(ll x,ll y){return x>y?x:y;} 11 12 int son[maxn][2],n,k,m,size; 13 ll val[maxn][2],maxv[maxn][2],minv[maxn][2],v[2],ans,pos; 14 15 struct p1{ll v[2];}a[maxn]; 16 bool vis[mod]; 17 int M(int x,int y){return (x*p+y)%mod;} 18 bool cmp0(p1 x,p1 y){return x.v[0]<y.v[0];} 19 bool cmp1(p1 x,p1 y){return x.v[1]<y.v[1];} 20 struct pt{int pos;ll ans;}; 21 struct _cmp{bool operator()(pt x,pt y)const{return x.ans<y.ans;}}; 22 23 priority_queue<pt,vector<pt>,_cmp>Q; 24 25 inline int read() 26 { 27 int w=0,q=0; 28 char c=getchar(); 29 while((c<'0' || c>'9') && c!='-') c=getchar(); 30 if (c=='-') q=1, c=getchar(); 31 while (c>='0' && c<='9') w=w*10+c-'0', c=getchar(); 32 return q ? -w : w; 33 } 34 35 void update(int x,int y) 36 { 37 for(int i=0;i<2;++i) 38 { 39 minv[x][i]=min(minv[x][i],minv[y][i]); 40 maxv[x][i]=max(maxv[x][i],maxv[y][i]); 41 } 42 } 43 void build(int l,int r,int dep,int num) 44 { 45 int mid=(l+r)>>1; 46 if(dep&1)nth_element(a+l,a+mid,a+r+1,cmp0); 47 else nth_element(a+l,a+mid,a+r+1,cmp1); 48 minv[num][0]=maxv[num][0]=val[num][0]=a[mid].v[0]; 49 minv[num][1]=maxv[num][1]=val[num][1]=a[mid].v[1]; 50 if(l<=mid-1) 51 { 52 build(l,mid-1,dep+1,son[num][0]=++size); 53 update(num,son[num][0]); 54 } 55 if(mid+1<=r) 56 { 57 build(mid+1,r,dep+1,son[num][1]=++size); 58 update(num,son[num][1]); 59 } 60 } 61 inline ll s(ll x){return x*x;} 62 inline ll f(int x) 63 { 64 if(x==0)return inf; 65 ll sum=0; 66 for(int i=0;i<2;++i) 67 { 68 if(v[i]<maxv[x][i])sum+=s(maxv[x][i]-v[i]); 69 if(minv[x][i]<v[i])sum+=s(minv[x][i]-v[i]); 70 } 71 return sum; 72 } 73 inline ll dis(int x){return s(val[x][0]-v[0])+s(val[x][1]-v[1]);} 74 void ask(int num,int g,ll&ans) 75 { 76 if(num==0)return; 77 ll d=dis(num); 78 if(!vis[M(g,num)]&&ans<d) 79 { 80 ans=d; 81 pos=num; 82 } 83 ll lf=f(son[num][0]),rf=f(son[num][1]); 84 if(lf>rf) 85 { 86 if(lf>ans)ask(son[num][0],g,ans); 87 if(rf>ans)ask(son[num][1],g,ans); 88 } 89 else 90 { 91 if(rf>ans)ask(son[num][1],g,ans); 92 if(lf>ans)ask(son[num][0],g,ans); 93 } 94 } 95 int main() 96 { 97 // freopen("a.in","r",stdin); 98 ios::sync_with_stdio(false); 99 n=read();k=read(); 100 for(int i=1;i<=n;++i)a[i].v[0]=read(),a[i].v[1]=read(); 101 build(1,n,1,size=1); 102 for(int i=1;i<=n;++i) 103 { 104 v[0]=a[i].v[0]; 105 v[1]=a[i].v[1]; 106 ans=-1; 107 ask(1,i,ans); 108 vis[M(i,pos)]=1; 109 Q.push((pt){i,ans}); 110 } 111 k=2*k; 112 ll hhh=0; 113 while(k--) 114 { 115 pt u=Q.top(); 116 Q.pop(); 117 hhh=u.ans; 118 v[0]=a[u.pos].v[0]; 119 v[1]=a[u.pos].v[1]; 120 ans=-1; 121 ask(1,u.pos,ans); 122 vis[M(u.pos,pos)]=1; 123 Q.push((pt){u.pos,ans}); 124 } 125 cout<<hhh<<endl; 126 return 0; 127 }
https://www.luogu.org/problemnew/show/P4357
5.两种操作,一种在平面上添加一个点,一种询问矩形区域内的权值和,允许离线。
离线建树,询问时判断某个点范围内的矩形是否在询问中即可。
1 #include<bits/stdc++.h> 2 using namespace std; 3 const int maxn=2E5+5; 4 const int inf=2000005; 5 const double d=0.75; 6 7 int n,m,size,cur,opt; 8 int son[maxn][2],val[maxn][2],sum[maxn],sumW[maxn],w[maxn],fa[maxn]; 9 10 struct rect{int l,r,u,d;}range[maxn]; 11 struct pt{int v[2];}a[maxn]; 12 struct query{int a[5];}Q[maxn]; 13 14 bool cmp0(pt x,pt y){return x.v[0]<y.v[0];} 15 bool cmp1(pt x,pt y){return x.v[1]<y.v[1];} 16 bool in(rect A,rect B){return (B.l<=A.l)&&(A.r<=B.r)&&(A.u<=B.u)&&(B.d<=A.d);} 17 bool out(rect A,rect B){return (B.r<A.l)||(A.r<B.l)||(B.u<A.d)||(A.u<B.d);} 18 bool inDot(rect A,int x,int y){return (A.l<=x)&&(x<=A.r)&&(A.d<=y)&&(y<=A.u);} 19 20 map<pair<int,int>,bool>vis; 21 map<pair<int,int>,int>where; 22 pair<int,int> M(int x,int y){return make_pair(x,y);} 23 24 void build(int l,int r,int dep,int num) 25 { 26 int mid=(l+r)>>1; 27 if(dep&1)nth_element(a+l,a+mid,a+r+1,cmp0); 28 else nth_element(a+l,a+mid,a+r+1,cmp1); 29 val[num][0]=a[mid].v[0]; 30 val[num][1]=a[mid].v[1]; 31 where[M(val[num][0],val[num][1])]=num; 32 // cout.width(14*dep-14); 33 // cout<<val[num][0]<<' '<<val[num][1]<<' '<<range[num].l<<' '<<range[num].r<<' '<<range[num].u<<' '<<range[num].d<<endl; 34 if(l<=mid-1) 35 { 36 son[num][0]=++size; 37 if(dep&1)range[son[num][0]]=(rect){range[num].l,val[num][0],range[num].u,range[num].d}; 38 else range[son[num][0]]=(rect){range[num].l,range[num].r,val[num][1],range[num].d}; 39 fa[size]=num; 40 build(l,mid-1,dep+1,size); 41 } 42 if(mid+1<=r) 43 { 44 son[num][1]=++size; 45 if(dep&1)range[son[num][1]]=(rect){val[num][0],range[num].r,range[num].u,range[num].d}; 46 else range[son[num][1]]=(rect){range[num].l,range[num].r,range[num].u,val[num][1]}; 47 fa[size]=num; 48 build(mid+1,r,dep+1,size); 49 } 50 } 51 int ask(rect R,int num) 52 { 53 if(num==0)return 0; 54 if(in(range[num],R))return sumW[num]; 55 else if(out(range[num],R))return 0; 56 int ans=0; 57 if(inDot(R,val[num][0],val[num][1]))ans+=w[num]; 58 return ask(R,son[num][0])+ask(R,son[num][1])+ans; 59 } 60 void add(int x,int num) 61 { 62 if(num==0)return; 63 sumW[num]+=x; 64 add(x,fa[num]); 65 } 66 int main() 67 { 68 ios::sync_with_stdio(false); 69 cin>>n>>n; 70 while(true) 71 { 72 cin>>opt; 73 if(opt==3)break; 74 ++m; 75 if(opt==1) 76 { 77 ++cur; 78 cin>>a[cur].v[0]>>a[cur].v[1]>>Q[m].a[3]; 79 Q[m].a[1]=a[cur].v[0]; 80 Q[m].a[2]=a[cur].v[1]; 81 Q[m].a[0]=1; 82 if(vis[M(a[cur].v[0],a[cur].v[1])])--cur; 83 vis[M(a[cur].v[0],a[cur].v[1])]=1; 84 } 85 else 86 { 87 Q[m].a[0]=2; 88 cin>>Q[m].a[1]>>Q[m].a[2]>>Q[m].a[3]>>Q[m].a[4]; 89 } 90 } 91 range[size=1]=(rect){1,n,n,1}; 92 build(1,cur,1,1); 93 for(int i=1;i<=m;++i) 94 { 95 if(Q[i].a[0]==1) 96 { 97 add(Q[i].a[3],where[M(Q[i].a[1],Q[i].a[2])]); 98 w[where[M(Q[i].a[1],Q[i].a[2])]]+=Q[i].a[3]; 99 } 100 else cout<<ask((rect){Q[i].a[1],Q[i].a[3],Q[i].a[4],Q[i].a[2]},1)<<endl; 101 } 102 return 0; 103 }
https://www.lydsy.com/JudgeOnline/problem.php?id=1176
6.两种操作,一种在矩形区域内点的权值加上给定值,一种询问历史最小值。
多维护个tag和minTag,由于修改是连续的,minTag相当于维护的是最小前缀和。