日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

K-D Tree学习笔记

發布時間:2023/12/3 编程问答 38 豆豆
生活随笔 收集整理的這篇文章主要介紹了 K-D Tree学习笔记 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

引入

K-D Tree 是一種處理高維空間的數據結構。

支持O(nk?1k)O(n^{\frac {k-1}k})O(nkk?1?)查詢給定超矩形內的點的信息, kkk 為維數。

可以用替罪羊樹的思想實現動態插入。

但其實用的最多的是在錯誤的復雜度內查詢奇奇怪怪的點信息。

構建

K-D Tree 是平衡樹的結構(但應該不能叫平衡樹)。

它的核心思想是通過樹一層層的分化,把整個空間分割成若干子空間。

但是點是kkk維的,而樹只有二維,所以一次我們只能選擇一維來劃分。

我們關心的問題是如何選取進行比較的維度。

一種方法是選取方差最大的維度,但在OI中不常用

OI中一般輪流選取,即深度為ddd的結點按第d%kd\%kd%k維的坐標劃分

具體而言,設點的序列為ppp,對于一個需要構建區間 [L,R][L,R][L,R],設當前需要分的維度為ddd

如果 L=RL=RL=R ,那么劃分出的只有當前一個點

為了保證盡量均勻,我們選取這一維的中位數作為當前子樹的根

設 mid=(l+r)/2

可以使用 C++ STL 中一個奧妙重重的函數 nth_element

它可以把第 kkk 小的元素放在第 kkk 位,然后比它小的放在左邊(但并不保證有序,后同),比它大的放在右邊。復雜度為 O(n)O(n)O(n) (其實就是閹割版的快排)

這樣我們 nth_element(p+l,p+mid,p+r+1) 就完事了

因為已經幫你分好了 ,所以把中位數間個點當根,然后遞歸 [L,mid?1][L,mid-1][L,mid?1][mid+1,R][mid+1,R][mid+1,R]

最后更新子樹信息,我們要維護的是超矩形,記錄一下每一維的最小值和最大值就可以了。

復雜度 O(nlog?n)O(n\log n)O(nlogn)

插入

用平衡樹的方法插入,根據當前層數判斷往左還是往右

當然可能會插成鏈,你又不能旋轉,所以只能考慮重構

暴力推平重新建就可以了,沒啥技術含量

只是重建帶一個 log?\loglog ,所以可能跑得有點慢……

詢問

詢問一個 kkk 維超矩形內所有點的信息

用線段樹的思想無腦遞歸就可以了

如果完全包含當前點劃分出的子空間直接返回當前子樹的值,沒有相交返回 000

復雜度可以證明是O(n1?1k)O(n^{1-\frac 1k})O(n1?k1?)

對于 k=2k=2k=2 的情況提供一個不知道對不對的證明:

對于一個結點,設其子樹大小為nnn,我們考慮往下分兩層


T1(n)T_1(n)T1?(n)表示詢問中間挖一塊的復雜度


T2(n)T_2(n)T2?(n)表示挖一個角的復雜度


T3(n)T_3(n)T3?(n)為平行挖的復雜度


顯然有

T1(n)=4T2(n4)T2(n)=2T3(n4)+T2(n4)T3(n)=2T3(n4)T_1(n)=4T_2(\frac n4)\\T_2(n)=2T_3(\frac n4)+T_2(\frac n4)\\T_3(n)=2T_3(\frac n4)T1?(n)=4T2?(4n?)T2?(n)=2T3?(4n?)+T2?(4n?)T3?(n)=2T3?(4n?)

解得

T1(n)=T2(n)=T3(n)=nT_1(n)=T_2(n)=T_3(n)=\sqrt nT1?(n)=T2?(n)=T3?(n)=n?

有一些變種,比如一次函數的下方、最近點對,但這些復雜度都是錯的。

不過如果出題人不刻意卡跑得還是挺快的,是個騙分的不錯選擇(

例題

Luogu4148 簡單題

有一個n×nn\times nn×n的方陣,開始時值都為000,維護mmm次操作:

  • (x,y)(x,y)(x,y)的位置加上vvv
  • 詢問一個矩陣內的數字和
  • n≤5×105,m≤2×105n\leq5\times10^5,m\leq2\times10^5n5×105,m2×105

    強制在線,空間限制20M

    模板題

    #include <iostream> #include <cstdio> #include <cstring> #include <cctype> #include <algorithm> #define MAXN 200005 using namespace std; inline int read() {int ans=0;char c=getchar();while (!isdigit(c)) c=getchar();while (isdigit(c)) ans=(ans<<3)+(ans<<1)+(c^48),c=getchar();return ans; } const double alpha=0.7; struct point{int x[2];point(const int& a=0,const int& b=0){x[0]=a,x[1]=b;}}; inline point min(const point& a,const point& b){return point(min(a.x[0],b.x[0]),min(a.x[1],b.x[1]));} inline point max(const point& a,const point& b){return point(max(a.x[0],b.x[0]),max(a.x[1],b.x[1]));} inline bool operator ==(const point& a,const point& b){return a.x[0]==b.x[0]&&a.x[1]==b.x[1];} int val[MAXN],sum[MAXN],siz[MAXN],ch[MAXN][2],tot; point cur[MAXN],L[MAXN],R[MAXN]; int rt,*tag,dir; int rub[MAXN]; inline int newnode(const int& v,const point& p) {int x=rub[0]? rub[rub[0]--]:++tot;val[x]=sum[x]=v,siz[x]=1,cur[x]=L[x]=R[x]=p;ch[x][0]=ch[x][1]=0;return x; } inline void update(const int& x) {sum[x]=val[x]+sum[ch[x][0]]+sum[ch[x][1]];siz[x]=siz[ch[x][0]]+siz[ch[x][1]]+1;L[x]=min(cur[x],min(L[ch[x][0]],L[ch[x][1]]));R[x]=max(cur[x],max(R[ch[x][0]],R[ch[x][1]])); } void insert(int& x,int k,int v,const point& p) {if (!x) return (void)(x=newnode(v,p));if (cur[x]==p) return val[x]+=v,update(x);int d=p.x[k]<cur[x].x[k];insert(ch[x][d],k^1,v,p);update(x);if (siz[ch[x][d]]>siz[x]*alpha) tag=&x,dir=k; } struct node{int v;point p;}; node buf[MAXN]; int cnt; int cur_k; inline bool operator <(const node& a,const node& b){return a.p.x[cur_k]<b.p.x[cur_k];} void build(int& x,int k,int l,int r) {if (l>r) return (void)(x=0);int mid=(l+r)>>1;cur_k=k,nth_element(buf+l,buf+mid,buf+r+1);x=newnode(buf[mid].v,buf[mid].p);build(ch[x][0],k^1,l,mid-1),build(ch[x][1],k^1,mid+1,r);update(x); } void dfs(int x) {if (!x) return;dfs(ch[x][0]);buf[++cnt].p=cur[x],buf[cnt].v=val[x],rub[++rub[0]]=x;dfs(ch[x][1]); } inline void rebuild() {cnt=0;dfs(*tag);build(*tag,dir,1,cnt); } int query(int x,point l,point r) {if (l.x[0]<=L[x].x[0]&&R[x].x[0]<=r.x[0]&&l.x[1]<=L[x].x[1]&&R[x].x[1]<=r.x[1]) return sum[x];if (r.x[0]<L[x].x[0]||R[x].x[0]<l.x[0]||r.x[1]<L[x].x[1]||R[x].x[1]<l.x[1]) return 0;int ans=0;if (l.x[0]<=cur[x].x[0]&&cur[x].x[0]<=r.x[0]&&l.x[1]<=cur[x].x[1]&&cur[x].x[1]<=r.x[1]) ans=val[x];return ans+query(ch[x][0],l,r)+query(ch[x][1],l,r); } int main() {memset(L,0x7f,sizeof(L));read();int lans=0;while (true){int x1,y1,x2,y2,v;switch(read()){case 1:x1=read()^lans,y1=read()^lans,v=read()^lans;tag=NULL;insert(rt,0,v,point(x1,y1));if (tag!=NULL) rebuild();break;case 2:x1=read()^lans,y1=read()^lans,x2=read()^lans,y2=read()^lans;printf("%d\n",lans=query(rt,point(x1,y1),point(x2,y2)));break;case 3:return 0;}} }

    Luogu4475 巧克力王國

    題意:給定nnnxi,yix_i,y_ixi?,yi?,mmm次詢問,每次給定a,b,ca,b,ca,b,c,求ax+by<cax+by<cax+by<c(x,y)(x,y)(x,y)的個數

    n,m≤5×104,?109≤x,y,a,b,c≤109n,m\leq 5\times 10^4,-10^9\leq x,y,a,b,c\leq10^9n,m5×104,?109x,y,a,b,c109

    先口胡一份

    顯然滿足條件的(x,y)(x,y)(x,y)在一個一次函數下方

    建出K-D Tree后,詢問時因為是矩形,如果四個點都在這個函數下方,那么整個矩形中的點都在下方,直接返回

    注意這個復雜度是錯誤的,只是數據沒卡跑得快

    總結

    以上是生活随笔為你收集整理的K-D Tree学习笔记的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。