模板:K-D tree
文章目錄
- 前言
- 思想
- 能解決的問題
- 操作
- 建樹
- 詢問
- 修改
- 完整代碼
所謂KDtree,就是有K個(gè)D的樹
(逃)
前言
KDtree是一種解決多維問題的暴力數(shù)據(jù)結(jié)構(gòu)
K維狀態(tài)下的最差復(fù)雜度是nk?1/kn^{k-1/k}nk?1/k
在最常見的2Dtree中就是根號n
但是玄學(xué)的它常常能到logn的復(fù)雜度
是騙分利器 (據(jù)說)
代碼實(shí)現(xiàn)很惡心,又長又長
和splay有一拼
甚至更加惡心
思想
按照每一維交錯(cuò)排序
不斷用超平面盡量的平分所有點(diǎn)
能解決的問題
通常是一些數(shù)據(jù)為二元組,對滿足某些偏序關(guān)系的數(shù)據(jù)進(jìn)行操作的問題
或者是各種經(jīng)典的二維平面第k近第k遠(yuǎn)之類的
操作
建樹
就是按照思想所說的模擬即可
使用O(n)的nth_element十分方便
詢問
經(jīng)典的KD樹的詢問就是尋找最近點(diǎn)
遞歸到一個(gè)結(jié)點(diǎn)時(shí)
首先用該點(diǎn)更新答案
然后算出兩個(gè)子樹的點(diǎn)范圍圍成的矩形到改點(diǎn)的最小值
計(jì)算的trick:
然后如果某個(gè)子樹的距離最小值比當(dāng)前答案大,可以直接跳過
否則先從最小值較小的開始嘗試搜索
代碼如下:
當(dāng)然,我們還要維護(hù)每個(gè)結(jié)點(diǎn)的子樹中的點(diǎn)的矩形的范圍:
void pushup(int k){int l=tr[k].ls,r=tr[k].rs;for(int i=0;i<=1;i++){tr[k].mn[i]=tr[k].mx[i]=tr[k].o.x[i];if(l){tr[k].mn[i]=min(tr[k].mn[i],tr[l].mn[i]);tr[k].mx[i]=max(tr[k].mx[i],tr[l].mx[i]);}if(r){tr[k].mn[i]=min(tr[k].mn[i],tr[r].mn[i]);tr[k].mx[i]=max(tr[k].mx[i],tr[r].mx[i]);}}tr[k].siz=tr[l].siz+tr[r].siz+1;return; }修改
考慮直接順著樹的性質(zhì)往下遞歸到空子樹建個(gè)新點(diǎn)即可:
void insert(int &k,int f,point u){if(!k){k=New(u);return;}if(u.x[f]<=tr[k].o.x[f]) insert(tr[k].ls,f^1,u);else insert(tr[k].rs,f^1,u);pushup(k);check(k,f);return; }代碼中的那個(gè)check是干啥的?
這樣在插入點(diǎn)規(guī)模過大時(shí)會(huì)使樹失衡,變成O(n)
所以我們仿照替罪羊樹的思路,設(shè)定常數(shù)x如果某個(gè)結(jié)點(diǎn)的一個(gè)子樹的大小超過總大小x倍,把整個(gè)子樹拍扁再重構(gòu)
所謂“拍扁”,就是把所有點(diǎn)結(jié)點(diǎn)放回一維數(shù)組中:
(rub數(shù)組用于回收廢點(diǎn))
然后重構(gòu)直接調(diào)用build就行了:
完整代碼
題目傳送門
#include<bits/stdc++.h> using namespace std; #define ll long long const int N=2e6+100; const int M=1050; const int mod=998244353; inline ll read(){ll x=0,f=1;char c=getchar();while(!isdigit(c)){if(c=='-') f=-1;c=getchar();}while(isdigit(c)){x=x*10+c-'0';c=getchar();}return x*f; } int n,m,tot,num; int rub[N],top; int F; struct point{int x[2];}p[N]; #define dis(a,b) (abs(a.x[0]-b.x[0])+abs(a.x[1]-b.x[1])) bool operator < (const point a,point b){return a.x[F]<b.x[F];} struct node{int mn[2],mx[2],ls,rs,siz;point o; }tr[N]; int New(point u){int x=top?rub[top--]:++tot;tr[x].o=u;tr[x].siz=1;tr[x].mn[0]=tr[x].mx[0]=u.x[0];tr[x].mn[1]=tr[x].mx[1]=u.x[1];tr[x].ls=tr[x].rs=0;return x; } void pushup(int k){int l=tr[k].ls,r=tr[k].rs;for(int i=0;i<=1;i++){tr[k].mn[i]=tr[k].mx[i]=tr[k].o.x[i];if(l){tr[k].mn[i]=min(tr[k].mn[i],tr[l].mn[i]);tr[k].mx[i]=max(tr[k].mx[i],tr[l].mx[i]);}if(r){tr[k].mn[i]=min(tr[k].mn[i],tr[r].mn[i]);tr[k].mx[i]=max(tr[k].mx[i],tr[r].mx[i]);}}tr[k].siz=tr[l].siz+tr[r].siz+1;return; } void print(int k){printf("k=%d (%d %d) ls=%d rs=%d mn=(%d %d) mx=(%d %d)\n",k,tr[k].o.x[0],tr[k].o.x[1],tr[k].ls,tr[k].rs,tr[k].mn[0],tr[k].mn[1],tr[k].mx[0],tr[k].mx[1]); } int build(int l,int r,int f){int mid=(l+r)>>1;F=f;nth_element(p+l,p+mid,p+r+1);int k=New(p[mid]);if(l<mid) tr[k].ls=build(l,mid-1,f^1);if(mid<r) tr[k].rs=build(mid+1,r,f^1);pushup(k);return k; } void pia(int k,int num){if(tr[k].ls) pia(tr[k].ls,num);p[num+tr[tr[k].ls].siz+1]=tr[k].o;if(tr[k].rs) pia(tr[k].rs,num+tr[tr[k].ls].siz+1);rub[++top]=k;return; } double A=0.75; void check(int &k,int f){if(tr[tr[k].ls].siz>tr[k].siz*A||tr[tr[k].rs].siz>tr[k].siz*A){pia(k,0);k=build(1,tr[k].siz,f);//printf("ok");}return; } void insert(int &k,int f,point u){if(!k){k=New(u);return;}if(u.x[f]<=tr[k].o.x[f]) insert(tr[k].ls,f^1,u);else insert(tr[k].rs,f^1,u);pushup(k);check(k,f);return; } int getdis(point o,int k){int res=0;for(int i=0;i<=1;i++){res+= max(0,tr[k].mn[i]-o.x[i])+max(0,o.x[i]-tr[k].mx[i]);}return res; } int ans; void query(int k,point now){int dl,dr,l=tr[k].ls,r=tr[k].rs;ans=min(ans,dis(tr[k].o,now));if(l) dl=getdis(now,tr[k].ls);if(r) dr=getdis(now,tr[k].rs);if(dl<dr){if(l&&dl<ans) query(tr[k].ls,now);if(r&&dr<ans) query(tr[k].rs,now);}else{if(r&&dr<ans) query(tr[k].rs,now);if(l&&dl<ans) query(tr[k].ls,now);} } int r; int main(){ // freopen("a.in","r",stdin); // freopen("a.out","w",stdout);n=read();m=read();for(int i=1;i<=n;i++){p[i]=(point){read(),read()};}r=build(1,n,0);for(int i=1;i<=m;i++){int op=read(),x=read(),y=read();point o=(point){x,y};if(op==1){insert(r,0,o);}else{ans=2e9;query(r,o);printf("%d\n",ans);}}return 0; } /**/thanks for reading!
總結(jié)
以上是生活随笔為你收集整理的模板:K-D tree的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: YBTOJ洛谷P4331:数字序列(左偏
- 下一篇: YBTOJ:染颜色(KDtree)