k-D Tree

作用

维护 \(k\) 维空间上的点的数据结构,树高严格 \(log\),空间 \(O(n)\)。支持插入,并如替罪羊树一般根据“不平衡度” \(\alpha\) 重构。

可以用线段树式或平衡树式实现。

建树

对于一个给定序列,类似线段树建树,使用 nth_element 可以总复杂度 \(n\log\) 建树。

由于需要分割,要么轮流按顺序维度分割,要么按方差较大的一位分(小优化?)。

修改

单点加入

一路走到叶子,类似替罪羊的拍平。可以 \(\alpha=0.6\)

区间修改

类似线段树,可以打标记,写 pushdown

删除节点

打标记,同时维护子树 size(未删点数),便于重构。

查询

核心是分成不交、完全计入贡献、部分有(继续递归)三个部分,类似线段树,但是复杂度玄学。

为了判断某个子树有没有进去的价值,需要 pushup 出子树内各个坐标的最值,便于判断在不在查询范围内/可能使答案更优。

代码

(杂交了一下)

带修

inline int New(node x){
	int u=hh?rub[hh--]:++tot;
	tr[u].val=x;
	tr[u].siz=1;
	tr[u].mn[0]=tr[u].mx[0]=x.px[0];
	tr[u].mn[1]=tr[u].mx[1]=x.px[1];
	tr[u].ls=tr[u].rs=0;
	return u;
}
inline void pushup(int u){
	int ls=tr[u].ls,rs=tr[u].rs;
	tr[u].siz=tr[ls].siz+tr[rs].siz+1;
	for(int i=0;i<K;++i){
		tr[u].mn[i]=tr[u].mx[i]=tr[u].val.px[i];
		if(ls){
			tr[u].mn[i]=min(tr[u].mn[i],tr[ls].mn[i]);
			tr[u].mx[i]=max(tr[u].mx[i],tr[ls].mx[i]);
		}
		if(rs){
			tr[u].mn[i]=min(tr[u].mn[i],tr[rs].mn[i]);
			tr[u].mx[i]=max(tr[u].mx[i],tr[rs].mx[i]);
		}
	}
}
inline int build(int l,int r,int d){
	int mid=l+r>>1;
	dm=d;
	nth_element(a+l,a+mid,a+r+1,cmp);
	int u=New(a[mid]);
	if(l<mid) tr[u].ls=build(l,mid-1,d^1);
	if(r>mid) tr[u].rs=build(mid+1,r,d^1);
	pushup(u);
	return u;
}
inline void pia(int u){
	if(tr[u].ls) pia(tr[u].ls);
	a[++num]=tr[u].val;
	if(tr[u].rs) pia(tr[u].rs);
	rub[++hh]=u;
}
inline void check(int &u,int d){
	if(tr[u].siz>3&&max(tr[tr[u].ls].siz,tr[tr[u].rs].siz)*5>tr[u].siz*3){
		num=0;pia(u);
		u=build(1,num,d);
	}
}
inline void insert(int &u,int d){
	if(!u){
		u=New(tx);
		return ;
	}
	if(tx.px[d]<=tr[u].val.px[d]) insert(tr[u].ls,d^1);
	else insert(tr[u].rs,d^1);
	pushup(u);check(u,d);
}

不带

inline void pushup(int u){
	int ls=tr[u].ls,rs=tr[u].rs;
	for(int i=0;i<K;++i){
		tr[u].mn[i]=tr[u].mx[i]=c[u].px[i];
		if(ls){
			tr[u].mn[i]=min(tr[u].mn[i],tr[ls].mn[i]);
			tr[u].mx[i]=max(tr[u].mx[i],tr[ls].mx[i]);
		}
		if(rs){
			tr[u].mn[i]=min(tr[u].mn[i],tr[rs].mn[i]);
			tr[u].mx[i]=max(tr[u].mx[i],tr[rs].mx[i]);
		}
	}
}
inline int build(int l,int r,int d){
	int mid=l+r>>1;
	nth_element(b+l,b+mid,b+r+1,[&](int x,int y){return c[x].px[d]<c[y].px[d];});
	int u=b[mid];
	if(l<mid) tr[u].ls=build(l,mid-1,d^1);
	if(mid<r) tr[u].rs=build(mid+1,r,d^1);
	pushup(u);
	return u;
}

模型

k近点对

矩形修,矩形查

k-D Tree分治/kdt上维护历史值

tips

下次学一下历史最大值。

posted @ 2024-02-01 09:47  mRXxy0o0  阅读(18)  评论(0)    收藏  举报