平衡树

KD-Tree

一个很好理解很好写用来骗分的数据结构。

K-D Tree 是一种可以高效处理 \(k\) 维空间信息的树形数据结构,同时具有平衡树的性质。

在 oi 中,常常是 \(k=2\),处理各种平面与点有关的问题。

性质

  1. 树的每一层都是按同一个关键字划分。
  2. 树的一颗子树可以划分出一个矩形。

建树(build)

常用轮转法进行划分,轮流选择 \(k\) 个维度,对于第 \(i\) 层划分的关键字是 \(i \bmod k\).

建树的复杂度是 \(\Theta(n \log n)\).

而为了维护建出的树比较平衡,运用了替罪羊树的重构思想进行。具体的,即引入一个重构常数 \(\alpha\),当一个节点的左儿子或右儿子占以当前节点为父节点的子树大小达到 \(\alpha\) 时,进行重构操作。

而利用 KD-Tree 是一棵平衡树的性质可以得到要重构的子树的中序遍历序列,从而进行重构操作。同时可以使用垃圾回收进行节点重复利用,这点与 fhq-Treap 是相同的。

但是 KD-Tree 要求高度为严格的 \(\log n+1\),而用替罪羊树实现 K-D Tree 只能做到 \(\Theta(\log n)\).

同时还要注意每次应该选择关键字维度的中位数进行划分,但在寻找中位数时若排序时间复杂度则为 \(\Theta(n \log n)\) 单次了,应用 nth_element 函数则可以优化到 \(\Theta(n)\).

void remove(int p,int cnt) {
	if(ls(p)) remove(ls(p),cnt);
	buf[++tot]=p;pt[tr[ls(p)].siz+cnt+1]=tr[p].p;
	if(rs(p)) remove(rs(p),cnt+tr[ls(p)].siz+1);
}

int rebuild(int l,int r,int k) {
	if(l>r) return 0;
	int mid=l+r>>1;
	int p=new_node();
	std::nth_element(pt+l,pt+mid,pt+r+1,[&](point a,point b) {
		return a.x[k]<b.x[k];
	});
	tr[p].p=pt[mid];
	ls(p)=rebuild(l,mid-1,k^1);rs(p)=rebuild(mid+1,r,k^1);
	push_up(p);return p;
}

插入(insert)

对于插入操作与平衡树一致,根据当前层划分依据决定要往左或右子树走即可。

而在每次插入操作之后要记得维护(maintain)树的形态,需要时进行重构,如上文所述。

void maintain(int &p,int k) {
	if(tr[p].siz*alpha<tr[ls(p)].siz || tr[p].siz*alpha<tr[rs(p)].siz) {
		remove(p,0);p=rebuild(1,tot,k);
	}
}

void insert(int &p,point cur,int k) {
	if(!p) {
		p=new_node();ls(p)=rs(p)=0;
		tr[p].p=cur;push_up(p);return;
	}
	if(cur.x[k]<=tr[p].p.x[k]) insert(ls(p),cur,k^1);
	else insert(rs(p),cur,k^1);
	push_up(p);maintain(p,k);
}

查询(query)

查询操作因题而异,灵活度较高。

P4148 简单题

由性质二,我们可以通过查询矩形与当前矩形的交的情况来进行查询。

#include <cstdio>
#include <algorithm>

const int N=5e5+10;

template<class T>

inline void read(T &val) {
	T x=0,f=1;char ch=getchar();
	while(ch<'0'||ch>'9') {ch=='-'?f=-1:0;ch=getchar();}
	while(ch>='0'&&ch<='9') {x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
	val=x*f;
}

template<class T>

inline void write(T x) {
	x<0?x=-x,putchar('-'):0;
	if(x>9) write(x/10);
	putchar(x%10^48);
}

struct KDTree {
	#define ls(p) tr[p].ls
	#define rs(p) tr[p].rs
	
	const double alpha=0.72;
	
	struct point {
		int x[2];
		int val;
	};
	
	struct node {
		int ls,rs;
		point p;
		int L[2],R[2];
		int sum,siz;
	}tr[N];
	
	int idx,rt;
	int buf[N],tot;
	
	int new_node() {
		if(tot) return buf[tot--];
		return ++idx;
	}
	
	void push_up(int p) {
		auto &cur=tr[p].p;
		for(int i=0;i<=1;i++) {
			tr[p].L[i]=std::min({tr[ls(p)].L[i],tr[rs(p)].L[i],cur.x[i]});
			tr[p].R[i]=std::max({tr[ls(p)].R[i],tr[rs(p)].R[i],cur.x[i]});
		}
		tr[p].sum=tr[ls(p)].sum+tr[rs(p)].sum+cur.val;
		tr[p].siz=tr[ls(p)].siz+tr[rs(p)].siz+1;
	}
	
	point pt[N];
	
	void remove(int p,int cnt) {
		if(ls(p)) remove(ls(p),cnt);
		buf[++tot]=p;pt[tr[ls(p)].siz+cnt+1]=tr[p].p;
		if(rs(p)) remove(rs(p),cnt+tr[ls(p)].siz+1);
	}
	
	int rebuild(int l,int r,int k) {
		if(l>r) return 0;
		int mid=l+r>>1;
		int p=new_node();
		std::nth_element(pt+l,pt+mid,pt+r+1,[&](point a,point b) {
			return a.x[k]<b.x[k];
		});
		tr[p].p=pt[mid];
		ls(p)=rebuild(l,mid-1,k^1);rs(p)=rebuild(mid+1,r,k^1);
		push_up(p);return p;
	}
	
	void maintain(int &p,int k) {
		if(tr[p].siz*alpha<tr[ls(p)].siz || tr[p].siz*alpha<tr[rs(p)].siz) {
			remove(p,0);p=rebuild(1,tot,k);
		}
	}
	
	void insert(int &p,point cur,int k) {
		if(!p) {
			p=new_node();ls(p)=rs(p)=0;
			tr[p].p=cur;push_up(p);return;
		}
		if(cur.x[k]<=tr[p].p.x[k]) insert(ls(p),cur,k^1);
		else insert(rs(p),cur,k^1);
		push_up(p);maintain(p,k);
	}
	
	int check(node cur,int x1,int y1,int x2,int y2) {
		if(cur.L[0]>=x1 && cur.R[0]<=x2 && cur.L[1]>=y1 && cur.R[1]<=y2) return 1;
		if(cur.R[0]<x1 || cur.L[0]>x2 || cur.R[1]<y1 || cur.L[1]>y2) return -1;
		return 0;
	}
	
	int check(point cur,int x1,int y1,int x2,int y2) {
		return cur.x[0]>=x1 && cur.x[0]<=x2 && cur.x[1]>=y1 && cur.x[1]<=y2;
	}
	
	int query(int p,int x1,int y1,int x2,int y2) {
		int fl=check(tr[p],x1,y1,x2,y2);
		if(fl==1) return tr[p].sum;
		if(fl==-1) return 0;
		int res=0;auto cur=tr[p].p;
		if(check(cur,x1,y1,x2,y2)) res+=cur.val;
		res+=query(ls(p),x1,y1,x2,y2);
		res+=query(rs(p),x1,y1,x2,y2);
		return res;
	}
}T;

int n;

int main() {
	read(n);
	auto &cur=T.tr[0];
	cur.L[0]=cur.L[1]=N+5;
	cur.R[0]=cur.R[1]=-1;
	int lst=0,op=0;
	while(1) {
		read(op);
		if(op==3) break;
		if(op==1) {
			int x=0,y=0,k=0;
			read(x);read(y);read(k);
			T.insert(T.rt,{x^lst,y^lst,k^lst},0);
		} else {
			int x1=0,y1=0,x2=0,y2=0;
			read(x1);read(y1);read(x2);read(y2);
			lst=T.query(T.rt,x1^lst,y1^lst,x2^lst,y2^lst);
			write(lst);putchar('\n');
		}
	}
	return 0;
}

参考文献

[1] https://www.cnblogs.com/Tenshi/p/15846105.html
[2] https://www.cnblogs.com/cmy-blog/p/kdtree.html
[3] https://www.cnblogs.com/YYYmoon/p/18655807

注:未完成。

posted @ 2025-02-16 17:51  ldh081122  阅读(48)  评论(0)    收藏  举报