KDT总结
咕咕咕。
学会了一点了。
KDT维护了\(k\)维空间中的超长方体。每个结点及其子树都在同一超长方体中。
KDT的实现与平衡树类似(其实在\(k=1\)时就是另类的平衡树,只不过不太优秀)。树上的每个结点都对应着\(k\)维空间中的一个点。然后随便维护一下信息就可以支持\(k\)维超长方体查询信息了。
还有类似线段树写法,用叶子存信息再向上合并的,但我只写过平衡树写法的。
建树时交替选择\(k\)个维度,每次把处于当前维度的中位数的元素拿出来(使用nth_element,这一步是\(O(n)\)的),再向左右递归。也可以用方差来判断选哪个维度,但感觉不太好写。
插入结点可以二进制分组。具体而言,保证维护的KDT的大小都为\(2^n\),于是插入结点就视作新建了一棵大小为\(1\)的KDT,然后依次将大小相同的KDT合并起来就好(实现的时候就是依次暴力展开然后暴力重构)。也可以根号重构/替罪羊树式重构,复杂度差不多的。
删除可以打标记惰性删除,然后根号/替罪羊重构。
每个结点维护每个维度的最大值与最小值,然后查询随便写一下就好(二进制分组的写法要在每棵树上都查一下)。
建树\(O(n\log n)\),查询\(O(n^{1-\frac{1}{k}})\),插入总的均摊是\(O(n\log^2 n)\)的(二进制分组)/\(O(n\sqrt{n\log n})\)(根号/替罪羊重构)。
OI中\(k=2\)的情况最多,但是还是可以考虑CDQ/树套树。当\(k=3\)时,要谨慎选择KDT,因为查一次就是\(O(n^{\frac{2}{3}})\)。
而当\(k\ge 4\)时,还是放弃KDT跑路吧。
为数不多还在坚持KDT的KDT板子题P4148 简单题。
板子
#include<bits/stdc++.h>
using namespace std;
const int maxn=2e5+10,mxlg=19;
int n,cnt,lst,b[maxn],rt[mxlg],lim[2][2];
struct TREE{
int x[2],v,sum,ls,rs,l[2],r[2];
}t[maxn];
bool cmp0(int a,int b){
return t[a].x[0]<t[b].x[0];
}
bool cmp1(int a,int b){
return t[a].x[1]<t[b].x[1];
}
#define ls(k) (t[k].ls)
#define rs(k) (t[k].rs)
void pushup(int k){
t[k].sum=t[k].v+t[ls(k)].sum+t[rs(k)].sum;
for(int i=0;i<2;++i){
t[k].l[i]=t[k].r[i]=t[k].x[i];
if(ls(k)){
t[k].l[i]=min(t[k].l[i],t[ls(k)].l[i]);
t[k].r[i]=max(t[k].r[i],t[ls(k)].r[i]);
}
if(rs(k)){
t[k].l[i]=min(t[k].l[i],t[rs(k)].l[i]);
t[k].r[i]=max(t[k].r[i],t[rs(k)].r[i]);
}
}
}
int build(int l,int r,int k){
int p=(l+r)>>1;
if(k) nth_element(b+l,b+p,b+r+1,cmp1);
else nth_element(b+l,b+p,b+r+1,cmp0);
int x=b[p];
if(l<p) t[x].ls=build(l,p-1,k^1);
if(p<r) t[x].rs=build(p+1,r,k^1);
pushup(x);
return x;
}
void flatten(int &p){
if(!p) return;
b[++cnt]=p;
flatten(ls(p));
flatten(rs(p));
p=0;
}
int qry(int p){
if(!p) return 0;
bool flag=true;
for(int k=0;k<2;++k) flag&=(lim[k][0]<=t[p].l[k]&&t[p].r[k]<=lim[k][1]);
if(flag) return t[p].sum;
for(int k=0;k<2;++k){
if(t[p].l[k]>lim[k][1]||t[p].r[k]<lim[k][0]) return 0;
}
int rs=0;
flag=true;
for(int k=0;k<2;++k) flag&=(lim[k][0]<=t[p].x[k]&&t[p].x[k]<=lim[k][1]);
if(flag) rs=t[p].v;
return rs+=qry(ls(p))+qry(rs(p));
}
int main(){
scanf("%d",&n);
n=0;
while(true){
int op;
scanf("%d",&op);
if(op==3) break;
else if(op==1){
int x,y,a;
scanf("%d%d%d",&x,&y,&a);
x^=lst;
y^=lst;
a^=lst;
t[++n].x[0]=x;
t[n].x[1]=y;
t[n].v=a;
cnt=1;
b[1]=n;
for(int sz=0;;sz++){
if(!rt[sz]){
rt[sz]=build(1,cnt,0);
break;
}
else flatten(rt[sz]);
}
}
else if(op==2){
scanf("%d%d%d%d",&lim[0][0],&lim[1][0],&lim[0][1],&lim[1][1]);
lim[0][0]^=lst;
lim[0][1]^=lst;
lim[1][0]^=lst;
lim[1][1]^=lst;
lst=0;
for(int i=0;i<mxlg;++i) lst+=qry(rt[i]);
printf("%d\n",lst);
}
}
return 0;
}

浙公网安备 33010602011771号