KD-Tree
Posted winniechen
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了KD-Tree相关的知识,希望对你有一定的参考价值。
KD-Tree
写在KD-Tree讲解之前,请先让我评论一番,“这什么垃圾算法!这也太垃圾了!”BY fcwww&Winniechen
BY GXZlegend KD-Tree,时间复杂度可证的可以被可持久化线段树替代,时间复杂度不可证的...时间复杂度不可证时间复杂度就是能被卡成n^2
但是,就算KD-Tree的时间复杂度是n^2,(卡起来很费劲,基本卡不到...)
怎么说呢,就是n^2的暴搜加剪枝...至于剪枝...(学过暴搜就会的那种...每道题不同,但是相差不大,基本就是设一个估价函数之后瞎写)
而有些剪枝之后的时间复杂度可证,其他不可证的就是没有剪枝那种。
另外,KD-Tree主要难卡的地方是每次将一个区间横着切一刀,之后再纵向切一刀,反正就是各种找中位数之后劈成两半,按照平衡树的存储方式存储,至于树高太高一般的解决方法是暴力重构(发现树高大于一个值之后重新建树...),或者是替罪羊树的重构方式(我不会...)
另外,KD-Tree不一定要按照010101的顺序切...(随机数什么的不也可以嘛...但是我没有写过,目测可行,而且更加卡不掉了...)
这种高级操作一般不会用到,用到更多的是插入导致深度过深之后的重构。
我们先讲一下KD-Tree的建树,每次以一维取中位数,作为这个节点,之后再分别建立左右子树。
中位数不会求?排序nlogn,总时间复杂度:O(nlog^2nK)。等等?只需要中位数?快排啊!O(n)取中位数。算了,好好说话,nth_element(tr+l,tr+m,tr+r+1,cmp);就张这个样子就可以了。具体实现是快排...
插入操作:BST的插入方式...我就不多说了...每次和这个节点的对应d进行比较之后排序就可以了。
查询操作:暴搜+剪枝,我不说了...遍历树+剪枝...
重构操作:找个随便的参数重构即可...(就是再建一遍树)
例题时间:
BZOJ2648: SJY摆棋子 & BZOJ2716: [Violet 3]天使玩偶
分析:KD-Tree入门题...查询曼哈顿距离最小,设一个估价函数(建议手画一下,推一推,也不是很难理解),时间复杂度严格O(nsqrt(n)),跑得比CDQ分治+树状数组快了不少...毕竟每层四个树状数组看的我头皮发麻...
附上代码:
#include <cstdio> #include <cmath> #include <cstring> #include <cstdlib> #include <iostream> #include <queue> #include <algorithm> using namespace std; #define N 1000005 #define inf 0x3f3f3f3f #define ls tr[rt].ch[0] #define rs tr[rt].ch[1] #define max(a,b) ((a)<(b)?(b):(a)) #define min(a,b) ((a)<(b)?(a):(b)) char buf[100000],*p1,*p2; inline char nc(){return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;} inline int rd() { register int x=0;register char ch=nc(); while(ch<‘0‘||ch>‘9‘) ch=nc(); while(ch>=‘0‘&&ch<=‘9‘) x=(x<<3)+(x<<1)+ch-‘0‘,ch=nc(); return x; } int rot,d,ans,n,Q; struct KDTree { int ch[2],maxn[2],minn[2],p[2]; friend bool operator<(const KDTree &a,const KDTree &b) { return a.p[d]==b.p[d]?a.p[!d]<b.p[!d]:a.p[d]<b.p[d]; } }tr[N]; #define PushUp(rt,s) tr[rt].minn[1]=min(tr[rt].minn[1],tr[s].minn[1]),tr[rt].minn[0]=min(tr[rt].minn[0],tr[s].minn[0]),tr[rt].maxn[0]=max(tr[rt].maxn[0],tr[s].maxn[0]),tr[rt].maxn[1]=max(tr[rt].maxn[1],tr[s].maxn[1]) int build(int l,int r,int flag) { int m=(l+r)>>1; d=flag,nth_element(tr+l,tr+m,tr+r+1); tr[m].maxn[0]=tr[m].minn[0]=tr[m].p[0]; tr[m].maxn[1]=tr[m].minn[1]=tr[m].p[1]; if(l<m)tr[m].ch[0]=build(l,m-1,flag^1),PushUp(m,tr[m].ch[0]); if(m<r)tr[m].ch[1]=build(m+1,r,flag^1),PushUp(m,tr[m].ch[1]); return m; } inline void insert(int x) { int *rt=&rot;d=0;int tmp=0; while(*rt)PushUp(*rt,x),rt=&tr[*rt].ch[tr[x].p[d]>tr[*rt].p[d]],d^=1,tmp++; *rt=x; if(tmp>=200)rot=build(1,n,0); } #define get_dis(rt,x,y) (max(tr[rt].minn[0]-x,0)+max(x-tr[rt].maxn[0],0)+max(tr[rt].minn[1]-y,0)+max(y-tr[rt].maxn[1],0)) void query(int rt,int x,int y) { int dis_rt=abs(x-tr[rt].p[0])+abs(y-tr[rt].p[1]); int dis_ls=ls?get_dis(ls,x,y):inf,dis_rs=rs?get_dis(rs,x,y):inf; ans=min(ans,dis_rt); if(dis_ls<dis_rs) { if(dis_ls<ans)query(ls,x,y); if(dis_rs<ans)query(rs,x,y); }else { if(dis_rs<ans)query(rs,x,y); if(dis_ls<ans)query(ls,x,y); } } int main() { n=rd();Q=rd(); for(int i=1;i<=n;i++)tr[i].p[0]=rd(),tr[i].p[1]=rd();rot=build(1,n,0); while(Q--) { int op=rd(),x=rd(),y=rd(); if(op==1)n++,tr[n].p[0]=tr[n].maxn[0]=tr[n].minn[0]=x,tr[n].p[1]=tr[n].maxn[1]=tr[n].minn[1]=y,insert(n); else ans=inf,query(rot,x,y),printf("%d ",ans); }return 0; }
BZOJ4066: 简单题
分析:如果不强制在线的话CDQ分治搞一搞很可以...但是显然,它强制在线...和上一题一样,时间复杂度可证的O(nsqrt(n)),具体证明我就不写了...查询的时候,类似Splay区间操作的存储方式,存一个子树和即可。
附上代码:
#include <cstdio> #include <cmath> #include <algorithm> #include <iostream> #include <queue> #include <cstdlib> #include <cstring> using namespace std; #define N 200005 #define ls tr[rt].ch[0] #define rs tr[rt].ch[1] int rot,d,last,n,Q; struct KDTree { int p[2],ch[2],maxn[2],minn[2],sum,w; friend bool operator<(const KDTree &a,const KDTree &b) { return a.p[d]==b.p[d]?a.p[!d]<b.p[!d]:a.p[d]<b.p[d]; } }tr[N]; void PushUp(int rt,int s) { tr[rt].sum+=tr[s].sum; tr[rt].maxn[0]=max(tr[rt].maxn[0],tr[s].maxn[0]); tr[rt].maxn[1]=max(tr[rt].maxn[1],tr[s].maxn[1]); tr[rt].minn[1]=min(tr[rt].minn[1],tr[s].minn[1]); tr[rt].minn[0]=min(tr[rt].minn[0],tr[s].minn[0]); } int build(int l,int r,int flag) { int m=(l+r)>>1,rt=m;d=flag;nth_element(tr+l,tr+m,tr+r+1);tr[m].ch[0]=tr[m].ch[1]=0; tr[m].maxn[0]=tr[m].minn[0]=tr[m].p[0];tr[m].maxn[1]=tr[m].minn[1]=tr[m].p[1];tr[m].sum=tr[m].w; if(l<m)ls=build(l,m-1,!flag),PushUp(m,ls);if(m<r)rs=build(m+1,r,!flag),PushUp(m,rs);return m; } void insert(int x) { int *rt=&rot;d=0; while(*rt)PushUp(*rt,x),rt=&tr[*rt].ch[tr[x].p[d]>tr[*rt].p[d]],d^=1; *rt=x; } int query(int rt,int x1,int y1,int x2,int y2) { if(!rt||tr[rt].maxn[0]<x1||tr[rt].maxn[1]<y1||tr[rt].minn[0]>x2||tr[rt].minn[1]>y2)return 0; if(tr[rt].maxn[0]<=x2&&tr[rt].maxn[1]<=y2&&tr[rt].minn[0]>=x1&&tr[rt].minn[1]>=y1)return tr[rt].sum; int ret=0;if(tr[rt].p[0]>=x1&&tr[rt].p[1]>=y1&&tr[rt].p[0]<=x2&&tr[rt].p[1]<=y2)ret+=tr[rt].w; ret+=query(ls,x1,y1,x2,y2)+query(rs,x1,y1,x2,y2);return ret; } int main() { scanf("%*d"); while(1) { int op,x,y,z,w;scanf("%d",&op);if(op==3)break; scanf("%d%d%d",&x,&y,&z);x^=last,y^=last,z^=last; if(op==1) { tr[++n].sum=tr[n].w=z; tr[n].p[0]=tr[n].maxn[0]=tr[n].minn[0]=x; tr[n].p[1]=tr[n].maxn[1]=tr[n].minn[1]=y; insert(n);if(n%10000==0)rot=build(1,n,0); } else scanf("%d",&w),w^=last,printf("%d ",last=query(rot,x,y,z,w)); }return 0; }
23333,跑不过CDQ分治...
BZOJ1941: [Sdoi2010]Hide and Seek
分析:KD-Tree基础题,两个估价函数,分别对应曼哈顿距离最大和最小
附上代码:
#include <cstdio> #include <cmath> #include <iostream> #include <queue> #include <algorithm> #include <cstring> #include <cstdlib> #include <bitset> using namespace std; #define N 500005 #define inf 2147483647 #define ls tr[rt].ch[0] #define rs tr[rt].ch[1] #define max(a,b) ((a)<(b)?(b):(a)) #define min(a,b) ((a)<(b)?(a):(b)) #define clear(rt) ls=rs=0,tr[rt].mx[0]=tr[rt].mn[0]=tr[rt].p[0],tr[rt].mx[1]=tr[rt].mn[1]=tr[rt].p[1] int rot,d,n,ans,ans1,ans2; char buf[100000],*p1,*p2; __attribute__((optimize("-O3")))inline char nc(){return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;} __attribute__((optimize("-O3")))inline int rd() { register int x=0;register char ch=nc(); while(ch<‘0‘||ch>‘9‘) ch=nc(); while(ch>=‘0‘&&ch<=‘9‘) x=(x<<3)+(x<<1)+ch-‘0‘,ch=nc(); return x; } struct KDTree{int ch[2],p[2],mx[2],mn[2]; friend bool operator<(const KDTree &a,const KDTree &b){return a.p[d]==b.p[d]?a.p[!d]<b.p[!d]:a.p[d]<b.p[d];}}tr[N]; #define PushUp(rt,s) tr[rt].mn[1]=min(tr[rt].mn[1],tr[s].mn[1]),tr[rt].mn[0]=min(tr[rt].mn[0],tr[s].mn[0]),tr[rt].mx[0]=max(tr[rt].mx[0],tr[s].mx[0]),tr[rt].mx[1]=max(tr[rt].mx[1],tr[s].mx[1]) __attribute__((optimize("-O3")))int build(int l,int r,int flag) { int rt=(l+r)>>1;d=flag;nth_element(tr+l,tr+rt,tr+r+1);clear(rt); if(l<rt)ls=build(l,rt-1,!flag),PushUp(rt,ls);if(rt<r)rs=build(rt+1,r,!flag),PushUp(rt,rs);return rt; } #define get_dis_min(rt,x,y) (max(tr[rt].mn[0]-x,0)+max(x-tr[rt].mx[0],0)+max(tr[rt].mn[1]-y,0)+max(y-tr[rt].mx[1],0)) #define get_dis_max(rt,x,y) (max(x-tr[rt].mn[0],0)+max(tr[rt].mx[0]-x,0)+max(y-tr[rt].mn[1],0)+max(tr[rt].mx[1]-y,0)) __attribute__((optimize("-O3")))void query_min(int rt,int x,int y) { int dis_rt=abs(tr[rt].p[0]-x)+abs(tr[rt].p[1]-y),dis_ls=ls?get_dis_min(ls,x,y):inf,dis_rs=rs?get_dis_min(rs,x,y):inf;if(dis_rt)ans1=min(dis_rt,ans1); if(dis_ls<dis_rs){if(dis_ls<ans1)query_min(ls,x,y);if(dis_rs<ans1)query_min(rs,x,y);}else{if(dis_rs<ans1)query_min(rs,x,y);if(dis_ls<ans1)query_min(ls,x,y);} } __attribute__((optimize("-O3")))void query_max(int rt,int x,int y) { int dis_rt=abs(tr[rt].p[0]-x)+abs(tr[rt].p[1]-y),dis_ls=ls?get_dis_max(ls,x,y):-inf,dis_rs=rs?get_dis_max(rs,x,y):-inf;ans2=max(dis_rt,ans2); if(dis_ls>dis_rs){if(dis_ls>ans2)query_max(ls,x,y);if(dis_rs>ans2)query_max(rs,x,y);}else{if(dis_rs>ans2)query_max(rs,x,y);if(dis_ls>ans2)query_max(ls,x,y);} } __attribute__((optimize("-O3")))int main() { n=rd();ans=inf; for(int i=1;i<=n;i++)tr[i].p[0]=rd(),tr[i].p[1]=rd();rot=build(1,n,0); for(int i=1;i<=n;i++){ans1=inf,ans2=-inf;query_min(rot,tr[i].p[0],tr[i].p[1]);query_max(rot,tr[i].p[0],tr[i].p[1]);ans=min(ans2-ans1,ans);} printf("%d ",ans);return 0; }
BZOJ2850: 巧克力王国
分析:说实话,这题KD-Tree的时间复杂度是可证的n^2...正解给的是似乎是半平面交+整体二分,显然我并不会...GXZlegend出的数据卡的我头皮发麻...不过,数据这么水就当是练一练KD-Tree了。
附上代码:
#include <cstdio> #include <cmath> #include <iostream> #include <queue> #include <algorithm> #include <cstring> #include <cstdlib> #include <bitset> using namespace std; #define N 50005 #define ll long long #define ls tr[rt].ch[0] #define rs tr[rt].ch[1] #define max(a,b) ((a)<(b)?(b):(a)) #define min(a,b) ((a)<(b)?(a):(b)) #define clear(rt) tr[rt].sum=tr[rt].w,ls=rs=0,tr[rt].mx[0]=tr[rt].mn[0]=tr[rt].p[0],tr[rt].mx[1]=tr[rt].mn[1]=tr[rt].p[1] int d,rot,n,Q;ll a,b,c; struct KDTree { int mx[2],p[2],ch[2],mn[2],w;ll sum; friend bool operator<(const KDTree &a,const KDTree &b) { return a.p[d]==b.p[d]?a.p[!d]<b.p[!d]:a.p[d]<b.p[d]; } }tr[N]; #define PushUp(rt,s) tr[rt].sum+=tr[s].sum,tr[rt].mn[1]=min(tr[rt].mn[1],tr[s].mn[1]),tr[rt].mn[0]=min(tr[rt].mn[0],tr[s].mn[0]),tr[rt].mx[0]=max(tr[rt].mx[0],tr[s].mx[0]),tr[rt].mx[1]=max(tr[rt].mx[1],tr[s].mx[1]) int build(int l,int r,int flag) { int rt=(l+r)>>1;d=flag;nth_element(tr+l,tr+rt,tr+r+1);clear(rt); if(l<rt)ls=build(l,rt-1,!flag),PushUp(rt,ls);if(rt<r)rs=build(rt+1,r,!flag),PushUp(rt,rs);return rt; } #define check(rt) (((a*tr[rt].mx[0]+b*tr[rt].mx[1])<c)+((a*tr[rt].mn[0]+b*tr[rt].mx[1])<c)+((a*tr[rt].mx[0]+b*tr[rt].mn[1])<c)+((a*tr[rt].mn[0]+b*tr[rt].mn[1])<c)) ll query(int rt) { int tmp;if(!rt||!(tmp=check(rt)))return 0;if(tmp==4)return tr[rt].sum; ll ret=0;if(((ll)tr[rt].p[0]*a+(ll)tr[rt].p[1]*b)<c)ret+=tr[rt].w; return ret+query(ls)+query(rs); } int main() { scanf("%d%d",&n,&Q); for(int i=1;i<=n;i++)scanf("%d%d%d",&tr[i].p[0],&tr[i].p[1],&tr[i].w);rot=build(1,n,0); while(Q--) { scanf("%lld%lld%lld",&a,&b,&c); printf("%lld ",query(rot)); }return 0; }
BZOJ3489: A simple rmq problem
分析:这题模型很好,考虑如果存在是什么情况,也就是上一次出现在l左边,下一次出现在r右边,那么将每一个a[i]建成一个三维的点,之后查询一个长方体内的点数,这题时间复杂度还是可证的...当然,如果不强制在线的话,我会考虑CDQ分治什么的...
附上代码:
#include <cstdio> #include <cmath> #include <iostream> #include <queue> #include <algorithm> #include <cstring> #include <cstdlib> #include <bitset> using namespace std; #define N 100005 #define inf 2147483647 #define ls tr[rt].ch[0] #define rs tr[rt].ch[1] #define max(a,b) ((a)<(b)?(b):(a)) #define min(a,b) ((a)<(b)?(a):(b)) #define clear(rt) for(int i=0;i<3;i++)tr[rt].mx[i]=tr[rt].mn[i]=tr[rt].p[i]; int rot,d,n,Q,ans,p[N],nxt[N],pre[N],a[N]; char buf[100000],*p1,*p2; inline char nc(){return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;} inline int rd() { register int x=0;register char ch=nc(); while(ch<‘0‘||ch>‘9‘) ch=nc(); while(ch>=‘0‘&&ch<=‘9‘) x=(x<<3)+(x<<1)+ch-‘0‘,ch=nc(); return x; } struct KDTree { int p[3],ch[2],mx[3],mn[3],maxx,w; friend bool operator<(const KDTree &a,const KDTree &b) { return a.p[d]==b.p[d]?(a.p[(d+1)%3]==b.p[(d+1)%3]?a.p[(d+2)%3]<b.p[(d+2)%3]:a.p[(d+1)%3]<b.p[(d+1)%3]):a.p[d]<b.p[d]; } }tr[N]; void PushUp(int rt,int s) { for(int i=0;i<3;i++) { tr[rt].mx[i]=max(tr[rt].mx[i],tr[s].mx[i]); tr[rt].mn[i]=min(tr[rt].mn[i],tr[s].mn[i]); }tr[rt].maxx=max(tr[rt].maxx,tr[s].maxx); } int build(int l,int r,int flag) { int rt=(l+r)>>1;d=flag;nth_element(tr+l,tr+rt,tr+r+1);for(int i=0;i<3;i++)tr[rt].mx[i]=tr[rt].mn[i]=tr[rt].p[i]; if(rt>l)ls=build(l,rt-1,(flag+1)%3),PushUp(rt,ls);if(rt<r)rs=build(rt+1,r,(flag+1)%3),PushUp(rt,rs);return rt; } int check(int rt,int x,int y) { if(tr[rt].maxx<=ans||tr[rt].mx[0]<x||tr[rt].mn[0]>y||tr[rt].mn[1]>=x||tr[rt].mx[2]<=y)return 0; return 1; } void query(int rt,int x,int y) { if(!rt||!check(rt,x,y))return ; if(tr[rt].p[0]>=x&&tr[rt].p[0]<=y&&tr[rt].p[1]<x&&tr[rt].p[2]>y)ans=max(ans,tr[rt].w); query(ls,x,y);query(rs,x,y); } int main() { n=rd();Q=rd(); for(int i=1,x;i<=n;i++) { x=rd();pre[i]=p[x];a[i]=x; if(p[x])nxt[p[x]]=i; p[x]=i; } for(int i=1;i<=n;i++)if(!nxt[i])nxt[i]=n+1; for(int i=1;i<=n;i++)tr[i].p[0]=i,tr[i].p[1]=pre[i],tr[i].p[2]=nxt[i],tr[i].w=tr[i].maxx=a[i]; rot=build(1,n,0); while(Q--) { int x=rd(),y=rd(); x=(x+ans)%n+1,y=(y+ans)%n+1; if(x>y)swap(x,y); ans=0;query(rot,x,y); printf("%d ",ans); }return 0; }
BZOJ2989: 数列 & BZOJ4170: 极光
分析:目测需要旋转坐标系...毕竟一个斜正方形的查询时间复杂度是不可证的,据说是O(n^2)的,但是呢,我就试了试,AC快乐!查询类似线段树的查询方式和4066一样,反正就是那么一个姿势...
附上代码:
#include <cstdio> #include <cmath> #include <cstring> #include <cstdlib> #include <iostream> #include <queue> #include <algorithm> using namespace std; #define N 100005 #define inf 0x3f3f3f3f #define ls tr[rt].ch[0] #define rs tr[rt].ch[1] #define max(a,b) ((a)<(b)?(b):(a)) #define min(a,b) ((a)<(b)?(a):(b)) #define clear(rt) ls=rs=0,tr[rt].mx[0]=tr[rt].mn[0]=tr[rt].p[0],tr[rt].mx[1]=tr[rt].mn[1]=tr[rt].p[1],tr[rt].sum=tr[rt].w; char buf[1000000],*p1,*p2; #define nc() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1000000,stdin),p1==p2)?EOF:*p1++) __attribute__((optimize("-O3")))inline int rd() { register int x=0;register char ch=nc(); while(ch<‘0‘||ch>‘9‘) ch=nc(); while(ch>=‘0‘&&ch<=‘9‘) x=(x<<3)+(x<<1)+ch-‘0‘,ch=nc(); return x; } int n,d,Q,k,a[N],rot; struct KDtree { int ch[2],mx[2],mn[2],p[2],sum,w; friend bool operator<(const KDtree &a,const KDtree &b) { return a.p[d]==b.p[d]?a.p[!d]<b.p[!d]:a.p[d]<b.p[d]; } }tr[N]; void PushUp(int rt,int s) { tr[rt].sum+=tr[s].sum; tr[rt].mx[0]=max(tr[rt].mx[0],tr[s].mx[0]); tr[rt].mx[1]=max(tr[rt].mx[1],tr[s].mx[1]); tr[rt].mn[1]=min(tr[rt].mn[1],tr[s].mn[1]); tr[rt].mn[0]=min(tr[rt].mn[0],tr[s].mn[0]); } int build(int l,int r,int flag) { int rt=(l+r)>>1;d=flag;nth_element(tr+l,tr+rt,tr+r+1);clear(rt); if(l<rt)ls=build(l,rt-1,!flag),PushUp(rt,ls);if(rt<r)rs=build(rt+1,r,!flag),PushUp(rt,rs);return rt; } void insert(int x) { int *rt=&rot;d=0; while(*rt)PushUp(*rt,x),rt=&tr[*rt].ch[tr[x].p[d]>tr[*rt].p[d]],d^=1; *rt=x; } #define get_dis_min(rt,x,y) (max(tr[rt].mn[0]-x,0)+max(x-tr[rt].mx[0],0)+max(tr[rt].mn[1]-y,0)+max(y-tr[rt].mx[1],0)) #define get_dis_max(rt,x,y) (max(x-tr[rt].mn[0],0)+max(tr[rt].mx[0]-x,0)+max(y-tr[rt].mn[1],0)+max(tr[rt].mx[1]-y,0)) int query(int rt,int x,int y) { if(!rt||get_dis_min(rt,x,y)>k)return 0; if(get_dis_max(rt,x,y)<=k)return tr[rt].sum;int ret=0; if(abs(tr[rt].p[0]-x)+abs(tr[rt].p[1]-y)<=k)ret+=tr[rt].w; return ret+query(ls,x,y)+query(rs,x,y); }char s[10]; int main() { scanf("%d%d",&n,&Q); for(int i=1,x;i<=n;i++)scanf("%d",&x),tr[i].p[0]=i,tr[i].p[1]=a[i]=x,tr[i].w=1;rot=build(1,n,0); while(Q--) { int x,y;scanf("%s%d%d",s,&x,&y); if(s[0]==‘Q‘)k=y,printf("%d ",query(rot,x,a[x])); else { tr[++n].p[0]=tr[n].mx[0]=tr[n].mn[0]=x; tr[n].p[1]=tr[n].mx[1]=tr[n].mn[1]=y; tr[n].w=tr[n].sum=1;a[x]=y;insert(n); } } return 0; }
BZOJ4520: [Cqoi2016]K远点对 & BZOJ2626: JZPFAR
分析:考虑暴力怎么求,维护一个有K个元素的小根堆,之后枚举n^2压入和弹出即可。那么KD-Tree干什么呢?剪枝!类似曼哈顿距离最大...只是变成了和堆顶比较...
附上代码:
4520:
#include <cstdio> #include <algorithm> #include <cmath> #include <cstring> #include <queue> #include <iostream> #include <cstdlib> using namespace std; #define N 100005 #define inf (1ll<<60) #define ll long long #define ls tr[rt].ch[0] #define rs tr[rt].ch[1] #define squ(x) ((ll)(x)*(x)) #define max(a,b) ((a)<(b)?(b):(a)) #define min(a,b) ((a)<(b)?(a):(b)) #define clear(rt) (tr[rt].mx[0]=tr[rt].mn[0]=tr[rt].p[0],tr[rt].mx[1]=tr[rt].mn[1]=tr[rt].p[1]) #define PushUp(rt,s) tr[rt].mx[0]=max(tr[rt].mx[0],tr[s].mx[0]),tr[rt].mx[1]=max(tr[rt].mx[1],tr[s].mx[1]),tr[rt].mn[1]=min(tr[rt].mn[1],tr[s].mn[1]),tr[rt].mn[0]=min(tr[rt].mn[0],tr[s].mn[0]) #define get_dis(rt,x,y) (squ(tr[rt].p[0]-x)+squ(tr[rt].p[1]-y)) #define pre_dis(rt,x,y) (max(squ(tr[rt].mn[0]-x),squ(tr[rt].mx[0]-x))+max(squ(tr[rt].mn[1]-y),squ(tr[rt].mx[1]-y))) #define nc() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1000000,stdin),p1==p2)?EOF:*p1++) char buf[1000000],*p1,*p2; __attribute__((optimize("-O3")))inline int rd() { register int x=0;register char ch=nc(); while(ch<‘0‘||ch>‘9‘) ch=nc(); while(ch>=‘0‘&&ch<=‘9‘) x=(x<<3)+(x<<1)+ch-‘0‘,ch=nc(); return x; } int d,rot,n,k;__attribute__((optimize("-O3")))priority_queue<ll>q; struct KDtree { int ch[2],p[2],mx[2],mn[2]; __attribute__((optimize("-O3")))friend bool operator<(const KDtree &a,const KDtree &b) { return a.p[d]==b.p[d]?a.p[!d]<b.p[!d]:a.p[d]<b.p[d]; } }tr[N]; __attribute__((optimize("-O3")))int build(int l,int r,int flag) { int rt=(l+r)>>1;d=flag;nth_element(tr+l,tr+rt,tr+r+1);clear(rt); if(l<rt)ls=build(l,rt-1,!flag),PushUp(rt,ls);if(rt<r)rs=build(rt+1,r,!flag),PushUp(rt,rs);return rt; } __attribute__((optimize("-O3")))void query(int rt,int x,int y) { ll dis_rt=get_dis(rt,x,y),dis_ls=ls?pre_dis(ls,x,y):-inf,dis_rs=rs?pre_dis(rs,x,y):-inf; if(-q.top()<dis_rt)q.pop(),q.push(-dis_rt); if(dis_ls>dis_rs){if(dis_ls>-q.top())query(ls,x,y);if(dis_rs>-q.top())query(rs,x,y);} else{if(dis_rs>-q.top())query(rs,x,y);if(dis_ls>-q.top())query(ls,x,y);} } __attribute__((optimize("-O3")))int main() { n=rd();k=rd();k=k<<1; for(int i=1;i<=n;i++)tr[i].p[0]=rd(),tr[i].p[1]=rd();rot=build(1,n,0); for(int i=1;i<=k;i++)q.push(0);for(int i=1;i<=n;i++)query(rot,tr[i].p[0],tr[i].p[1]); printf("%lld ",-q.top()); }
2626:
#include <cstdio> #include <algorithm> #include <cmath> #include <cstdlib> #include <cstring> #include <iostream> #include <queue> using namespace std; #define N 100005 #define inf (1ll<<60) #define ll long long #define ls tr[rt].ch[0] #define rs tr[rt].ch[1] #define squ(x) ((ll)(x)*(x)) #define max(a,b) ((a)<(b)?(b):(a)) #define min(a,b) ((a)<(b)?(a):(b)) #define clear(rt) (tr[rt].mx[0]=tr[rt].mn[0]=tr[rt].p[0],tr[rt].mx[1]=tr[rt].mn[1]=tr[rt].p[1]) #define PushUp(rt,s) tr[rt].mx[0]=max(tr[rt].mx[0],tr[s].mx[0]),tr[rt].mx[1]=max(tr[rt].mx[1],tr[s].mx[1]),tr[rt].mn[1]=min(tr[rt].mn[1],tr[s].mn[1]),tr[rt].mn[0]=min(tr[rt].mn[0],tr[s].mn[0]) #define get_dis(rt,x,y) (squ(tr[rt].p[0]-x)+squ(tr[rt].p[1]-y)) #define pre_dis(rt,x,y) (max(squ(tr[rt].mn[0]-x),squ(tr[rt].mx[0]-x))+max(squ(tr[rt].mn[1]-y),squ(tr[rt].mx[1]-y))) #define nc() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1000000,stdin),p1==p2)?EOF:*p1++) char buf[1000000],*p1,*p2; __attribute__((optimize("-O3")))inline int rd() { register int x=0,f=1;register char ch=nc(); while(ch<‘0‘||ch>‘9‘){if(ch==‘-‘)f=-1;ch=nc();} while(ch>=‘0‘&&ch<=‘9‘)x=(x<<3)+(x<<1)+ch-‘0‘,ch=nc(); return x*f; } int d,rot,n,Q;priority_queue<pair<ll,int> >q; struct KDtree{int p[2],mn[2],mx[2],idx,ch[2];}tr[N]; __attribute__((optimize("-O3")))bool cmp(const KDtree &a,const KDtree &b){return a.p[d]==b.p[d]?a.p[!d]<b.p[!d]:a.p[d]<b.p[d];} __attribute__((optimize("-O3")))int build(int l,int r,int flag) { int rt=(l+r)>>1;d=flag;nth_element(tr+l,tr+rt,tr+r+1,cmp);clear(rt); if(l<rt)ls=build(l,rt-1,!flag),PushUp(rt,ls);if(rt<r)rs=build(rt+1,r,!flag),PushUp(rt,rs);return rt; } __attribute__((optimize("-O3")))void query(int rt,int x,int y) { ll dis_rt=get_dis(rt,x,y),dis_ls=ls?pre_dis(ls,x,y):-inf,dis_rs=rs?pre_dis(rs,x,y):-inf; q.push(make_pair(-dis_rt,tr[rt].idx));q.pop(); if(dis_ls>dis_rs){if(dis_ls>=-q.top().first)query(ls,x,y);if(dis_rs>=-q.top().first)query(rs,x,y);} else {if(dis_rs>=-q.top().first)query(rs,x,y);if(dis_ls>=-q.top().first)query(ls,x,y);} } __attribute__((optimize("-O3")))int main() { n=rd();for(int i=1;i<=n;i++)tr[i].p[0]=rd(),tr[i].p[1]=rd(),tr[i].idx=i;rot=build(1,n,0); Q=rd();while(Q--) { int x=rd(),y=rd(),z=rd();while(!q.empty())q.pop(); for(int i=1;i<=z;i++)q.push(make_pair(0,1<<30)); query(rot,x,y);printf("%d ",q.top().second); }return 0; }
那么先讲到这里吧...毕竟什么崂山白水我也不会...替罪羊树我也不会...我也就没有写...得了得了...不多说了,去做KD-Tree的课件了...过不了多久就会上传的!
以上是关于KD-Tree的主要内容,如果未能解决你的问题,请参考以下文章