【高级数据结构】K-D Tree
Paul·Shi
·
·
题解
【高级数据结构】K-D Tree
K-D Tree 是用来解决K维空间中数点问题强有力的数据结构,可以在 (NlogN) ——(N\sqrt{N}) 的时间复杂度内完成查询和修改。
一、K-D Tree的做法
K-D Tree当K等于 1 时,就是一颗替罪羊树树(平衡树的一种)。
我们把K-D Tree扩展到 K 维空间。
K-D Tree是一颗权值树,我们对于每个树的结点用一个结构体来存
struct K_D_Tree{
int l,r,sum,val,size,Min[K],Max[K],d[K];
}tr[MAXN];
$sum$ 为以该点为根的子树区间中点权之和
$val$ 为该点所存的点的点权
$size$ 为该树为根的数中的点数
$Min[i]$ 表示在第 $i$ 维上区间的下界
$Max[i]$ 表示在第 $i$ 维上区间的上界
$d[i]$ 表示该位置表示的点的第 $i$ 维的坐标
### 插入操作
我们对于深度为 $i$ 的位置,我们按照 第 $i%k$ 维的坐标来排序、
然后就是普通平衡树的插入即可
我们设定一个**平衡因子**,一般设定为 &0.6~0.9$ 之间
```cpp
const double alpha=0.75;
```
当在插入是发现要插入的子树的 $size$ 比整棵树的 $size$ 的 $alpha$ 倍要大,即发现树不平衡,那么我们直接暴力重建该子树。
### 查询操作
直接平衡树的查询操作即可
### 删除操作
我们直接对要删除的点打删除标记,在插入时再删除,在查询或删除时时如果发现某颗子树的的删除标记个数大于一个定值,我们对于该子树暴力重构即可。
## 二、K-D Tree的几何性质

可以看上面一个以 $k$ 为 $2$ 为例的图
$K-D Tree$ 相当于每个结点对于其管辖的区间平行于坐标轴分割成一半,最后整个图被分成若干个区间,但这些区间的大小是不一致的,所以 $K-D Tree$ 是很容易被卡的。
# 模板题:
三维偏序:P4148 简单题
```cpp
#include<bits/stdc++.h>
#define MAXN 100010
using namespace std;
inline int read ()
{
int s=0,w=1;
char ch=getchar ();
while (ch<'0'||ch>'9'){if (ch=='-') w=-1;ch=getchar ();}
while ('0'<=ch&&ch<='9') s=(s<<1)+(s<<3)+(ch^48),ch=getchar ();
return s*w;
}
const double alpha=0.75;
const int K=2;
struct K_D_Tree{
int l,r,sum,val,size,Min[K],Max[K],d[K];
}tr[MAXN];
int n,ans,root,len;
int p[K],q[K][2],A;
int D,num,h[MAXN];
bool cmp (const int &a,const int &b)
{
return tr[a].d[D]<tr[b].d[D];
}
inline void update (int x)
{
int l=tr[x].l,r=tr[x].r;
tr[x].size=tr[l].size+tr[r].size+1;
tr[x].sum=tr[l].sum+tr[r].sum+tr[x].val;
for (int i=0;i<K;i++)
{
if (l) tr[x].Max[i]=max (tr[l].Max[i],tr[x].Max[i]),tr[x].Min[i]=min (tr[l].Min[i],tr[x].Min[i]);
if (r) tr[x].Max[i]=max (tr[r].Max[i],tr[x].Max[i]),tr[x].Min[i]=min (tr[r].Min[i],tr[x].Min[i]);
}
}
inline void build (int &x,int l,int r,int k)
{
if (l>r) return;
int mid=(l+r)>>1;D=k;
nth_element (h+l,h+mid+1,h+r+1,cmp);
x=h[mid];
tr[x].sum=tr[x].val;
for (int i=0;i<K;i++) tr[x].Max[i]=tr[x].Min[i]=tr[x].d[i];
build (tr[x].l,l,mid-1,(k+1)%K);
build (tr[x].r,mid+1,r,(k+1)%K);
update (x);
}
inline void erase (int &x)
{
if (!x) return;
h[++num]=x;
erase (tr[x].l),erase (tr[x].r);
x=0;
}
inline void rebuild (int &x,int k)
{
h[num=1]=++len;
tr[len].size=1;
for (int i=0;i<K;i++) tr[len].d[i]=p[i];
tr[len].val=tr[len].sum=A;
erase (x),build (x,1,num,k);
}
inline void insert (int &x,int k)
{
if (!x)
{
tr[x=++len].size=1,tr[x].val=tr[x].sum=A;
for (int i=0;i<K;i++) tr[x].Max[i]=tr[x].Min[i]=tr[x].d[i]=p[i];
return;
}
if (p[k]<tr[x].d[k])
{
if (tr[tr[x].l].size>tr[x].size*alpha) rebuild (x,k);
else insert (tr[x].l,(k+1)%K);
}
else
{
if (tr[tr[x].r].size>tr[x].size*alpha) rebuild (x,k);
else insert (tr[x].r,(k+1)%K);
}
update (x);
}
inline bool check_range (int x)
{
if (!x) return 0;
for (int i=0;i<K;i++)
if (q[i][0]>tr[x].Min[i]||q[i][1]<tr[x].Max[i]) return 0;
return 1;
}
inline bool check_point (int x)
{
if (!x) return 0;
for (int i=0;i<K;i++)
if (tr[x].d[i]<q[i][0]||tr[x].d[i]>q[i][1]) return 0;
return 1;
}
inline bool check (int x)
{
if (!x) return 0;
for (int i=0;i<K;i++)
if (q[i][1]<tr[x].Min[i]||q[i][0]>tr[x].Max[i]) return 0;
return 1;
}
inline void query (int x)
{
if (check_range (x))
{
ans+=tr[x].sum;
return;
}
if (check_point (x)) ans+=tr[x].val;
if (check (tr[x].l)) query (tr[x].l);
if (check (tr[x].r)) query (tr[x].r);
}
int main()
{
n=read ();
while (1)
{
int opt=read ();
if (opt==1)
{
for (int i=0;i<K;i++) p[i]=read ()^ans;
A=read ()^ans;
insert (root,0);
}
if (opt==2)
{
for (int i=0;i<=1;i++)
for (int j=0;j<K;j++) q[j][i]=read ()^ans;
ans=0;query (root);
printf ("%d\n",ans);
}
if (opt==3) break;
}
return 0;
}
```