KDT总结

咕咕咕。

学会了一点了。

KDT维护了k维空间中的超长方体。每个结点及其子树都在同一超长方体中。

KDT的实现与平衡树类似(其实在k=1时就是另类的平衡树,只不过不太优秀)。树上的每个结点都对应着k维空间中的一个点。然后随便维护一下信息就可以支持k维超长方体查询信息了。

还有类似线段树写法,用叶子存信息再向上合并的,但我只写过平衡树写法的。

建树时交替选择k个维度,每次把处于当前维度的中位数的元素拿出来(使用nth_element,这一步是O(n)的),再向左右递归。也可以用方差来判断选哪个维度,但感觉不太好写。

插入结点可以二进制分组。具体而言,保证维护的KDT的大小都为2n,于是插入结点就视作新建了一棵大小为1的KDT,然后依次将大小相同的KDT合并起来就好(实现的时候就是依次暴力展开然后暴力重构)。也可以根号重构/替罪羊树式重构,复杂度差不多的。

删除可以打标记惰性删除,然后根号/替罪羊重构。

每个结点维护每个维度的最大值与最小值,然后查询随便写一下就好(二进制分组的写法要在每棵树上都查一下)。

建树O(nlogn),查询O(n11k),插入总的均摊是O(nlog2n)的(二进制分组)/O(nnlogn)(根号/替罪羊重构)。

OI中k=2的情况最多,但是还是可以考虑CDQ/树套树。当k=3时,要谨慎选择KDT,因为查一次就是O(n23)

而当k4时,还是放弃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;
} 
posted @   RandomShuffle  阅读(11)  评论(0编辑  收藏  举报
编辑推荐:
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· 写一个简单的SQL生成工具
· AI 智能体引爆开源社区「GitHub 热点速览」
· C#/.NET/.NET Core技术前沿周刊 | 第 29 期(2025年3.1-3.9)
点击右上角即可分享
微信分享提示