浅谈K-Dtree

前言

发现自己的树套树太菜了
cdq嵌套也太菜了
面对多维的问题直接上天
于是就去学了下K-Dtree,发现不是很难的亚子


K-Dtree的本质就是一颗二叉搜索树,一般的二叉搜索树是按数的大小来划分左右儿子的

K-Dtree就是每次随机(或者轮换)按照一维来划分成二叉搜索树的左右儿子,实现很简单。
假设现在要把a[i]建成一颗K-Dtree
首先随便按照a[i]的一维排序。
然后取中间那个作为根,分成左右部分作为两颗子树,递归下去建树

但是要排序很不优美,c++中有一个好用的stl叫做nth_element,具体的用法是

nth_element(a + l, a + mid, a + r + 1, cmp)

表示的就是a[mid]就是a数组的中位数,比中位数小的在a[l…mid-1],大的在a[mid+1…r]非常方便

建树就是场这样

int build(int l, int r, int o) { //o表示按照那一维划分
	if(l > r) return 0;
	int mid = (l + r) >> 1;
	if(!o) nth_element(a + l, a + mid, a + r + 1, cmp1) ;
	else nth_element(a + l, a + mid, a + r + 1, cmp2);//划分
	ch[mid][0] = build(l, mid - 1, o ^ 1);//递归下去建树
	ch[mid][1] = build(mid + 1, r, o ^ 1);
	update(mid);
	return mid;
}

可能会有人问?这样怎么就可以保证时间复杂度
其实好像也不能保证,但是基本卡不掉

当然最好就是每一维算出方差,取方差最大的一维来划分,写起来麻烦一些而已
这种划分好像是 O ( n 2 − 1 D ) \large O(n^{2-\frac{1}{D}}) O(n2D1), D是维数

然后来集体练习一下

luogu P4357 [CQOI2016]K远点对

这题用K-Dtree的时间复杂度是假的
但是可以当作入门题来练练。

K-Dtree有个重要的地方就是方便剪枝
这题就维护子树范围内最大最小的横纵坐标,算可能最大的情况作为估值
比较左右子树的估值,哪边估值大就先往哪边跑。
如果估值<=已知最大的就没有必要跑了

code;

#include
#define N 1000005
#define int long long
using namespace std;
struct A {
	int x, y;
} a[N];
int cmp1(A x, A y) {
	return x.x < y.x;
}
int cmp2(A x, A y) {
	return x.y < y.y;
}
int L[N], R[N], D[N], U[N], ch[N][2], n, k;
void update(int x) {
	L[x] = R[x] = a[x].x;
	D[x] = U[x] = a[x].y;
	if(ch[x][0]) {
		L[x] = min(L[x], L[ch[x][0]]), R[x] = max(R[x], R[ch[x][0]]);
		D[x] = min(D[x], D[ch[x][0]]), U[x] = max(U[x], U[ch[x][0]]);	
	} 
	if(ch[x][1]) {
		L[x] = min(L[x], L[ch[x][1]]), R[x] = max(R[x], R[ch[x][1]]);
		D[x] = min(D[x], D[ch[x][1]]), U[x] = max(U[x], U[ch[x][1]]);	
	} 
}
int build(int l, int r, int o) {
	if(l > r) return 0;
	int mid = (l + r) >> 1;
	if(!o) nth_element(a + l, a + mid, a + r + 1, cmp1);
	else nth_element(a + l, a + mid, a + r + 1, cmp2);
	ch[mid][0] = build(l, mid - 1, o ^ 1);
	ch[mid][1] = build(mid + 1, r, o ^ 1);
	update(mid);
	return mid;
}
int pf(int x) { return x * x;}
int getmax(int x, int y) {
	return max(pf(a[x].x - L[y]), pf(a[x].x - R[y])) + max(pf(a[x].y - D[y]), pf(a[x].y - U[y]));
}
priority_queue<int, vector<int>, greater<int> > q;
void query(int l, int r, int id) {
	if(l > r) return;
	int mid = (l + r) >> 1, t = pf(a[mid].x - a[id].x) + pf(a[mid].y - a[id].y);
	if(t > q.top()) q.pop(), q.push(t);
	int disl = getmax(id, ch[mid][0]), disr = getmax(id, ch[mid][1]);
	if(disl > disr) {
		if(disl > q.top()) query(l, mid - 1, id);
		if(disr > q.top()) query(mid + 1, r, id);
	} else {
		if(disr > q.top()) query(mid + 1, r, id);
		if(disl > q.top()) query(l, mid - 1, id);
	}
}
signed main() {
	scanf("%lld%lld", &n, &k); k <<= 1;
	for(int i = 1; i <= k; i ++) q.push(0);
	for(int i = 1; i <= n; i ++) scanf("%lld%lld", &a[i].x, &a[i].y);
	build(1, n, 0);
	for(int i = 1; i <= n; i ++) query(1, n, i);
	printf("%lld", q.top());
	return 0;
}

看代码能懂。

P3769 [CH弱省胡策R2]TATT

可以说是四维偏序板子题
cdq套cdq套cdq好像写起来很麻烦
其他的解法好像也很麻烦
而且要三个log

可以考虑K-Dtree

但是这题还要插入
直接插入的话就会不平衡,考虑如何让它平衡
K-Dtree是不支持旋转的,所以可以用类似替罪羊树的思想拍扁重建

然后就没了

平衡因子好像比较玄学

code:


#include
#define N 1000005
using namespace std;
int mi[N][5], ma[N][5], mf[N], f[N], sz, ch[N][2], size[N], ls[N], tot, dd, root, d[N];
struct T {
	int a[5];
} t[N];
void update(int x) {
	mi[x][0] = ma[x][0] = t[x].a[0];
	mi[x][1] = ma[x][1] = t[x].a[1];
	mi[x][2] = ma[x][2] = t[x].a[2];
	mf[x] = f[x];
	if(ch[x][0]) {
		mf[x] = max(mf[x], mf[ch[x][0]]);
		for(int i = 0; i < 3; i ++)
			mi[x][i] = min(mi[x][i], mi[ch[x][0]][i]),
			ma[x][i] = max(ma[x][i], ma[ch[x][0]][i]);
	}
	if(ch[x][1]) {
		mf[x] = max(mf[x], mf[ch[x][1]]);
		for(int i = 0; i < 3; i ++)
			mi[x][i] = min(mi[x][i], mi[ch[x][1]][i]),
			ma[x][i] = max(ma[x][i], ma[ch[x][1]][i]);
	}
	size[x] = size[ch[x][0]] + size[ch[x][1]] + 1;
}
void dfs(int x) {
	if(!x) return;
	dfs(ch[x][0]);
	ls[++ sz] = x;
	dfs(ch[x][1]);
}
int cmpp(int x, int y) {
	return t[x].a[dd] < t[y].a[dd];
}
int build(int l, int r, int o) {
	if(l > r) return 0;
	int mid = (l + r) >> 1;
	dd = o;
	nth_element(ls + l, ls + mid, ls + r + 1, cmpp);
	int rt = ls[mid];
	d[rt] = o;
	ch[rt][0] = build(l, mid - 1, (o + 1) % 3);
	ch[rt][1] = build(mid + 1, r, (o + 1) % 3);
	update(rt);
	return rt;
}
void rebuild(int &x) {
	sz = 0;
	dfs(x);
	x = build(1, sz, 0);
}
const double alpha = 0.725;
bool bad(int x) {return alpha * size[x] <= (double)max(size[ch[x][0]], size[ch[x][1]]);}
void insert(int &x, int y) {
	if(!x) {
		x = y;
		update(x);
		return;
	}
	if(t[y].a[d[x]] <= t[x].a[d[x]]) insert(ch[x][0], y);
	else insert(ch[x][1], y);
	update(x);
	if(bad(x)) rebuild(x);
}
int ans = 0;
void query(int x, int y) { 
	if(!x || t[y].a[0] < mi[x][0] || t[y].a[1] < mi[x][1] || t[y].a[2] < mi[x][2]) return ; 
	if(t[x].a[0] <= t[y].a[0] && t[x].a[1] <= t[y].a[1] && t[x].a[2] <= t[y].a[2])ans = max(ans, f[x]);
	if(mf[x] <= ans) return;
	if(ma[x][0] <= t[y].a[0] && ma[x][1] <= t[y].a[1] && ma[x][2] <= t[y].a[2]) {
		ans = max(ans, mf[x]);
		return;
	}
	query(ch[x][0], y), query(ch[x][1], y);
}
int n;
struct A {
	int a[5];
} a[N];
int cmp(A x, A y) {
	if(x.a[0] != y.a[0]) return x.a[0] < y.a[0];
	if(x.a[1] != y.a[1]) return x.a[1] < y.a[1];
	if(x.a[2] != y.a[2]) return x.a[2] < y.a[2];
	return x.a[3] < y.a[3];
}
int main() {
	srand(time(NULL));
	scanf("%d", &n);
	for(int i = 1; i <= n; i ++) scanf("%d%d%d%d", &a[i].a[0], &a[i].a[1], &a[i].a[2], &a[i].a[3]);
	sort(a + 1, a + 1 + n, cmp);
 	for(int i = 1; i <= n; i ++) {
 		t[++ tot] = T{a[i].a[1], a[i].a[2], a[i].a[3]};
 		ans = 0;
 		query(root, tot);
		f[tot] = ans + 1;
 		insert(root, tot);
	}
		
	ans = 0;
	for(int i = 1; i <= n; i ++) ans = max(ans, f[i]);
	printf("%d", ans);
	return 0;
}

P4475 巧克力王国

这题关键也是在估值函数上
如果子树最大的x和y都不行就不用进入子树了

然后就没了

code:

#include
#define N 1000005
#define int long long
using namespace std;
int mi[N][5], ma[N][5], mf[N], f[N], sz, ch[N][2], size[N], ls[N], tot, dd, root, d[N], m, n;
struct T {
	int a[5];
} t[N];
void update(int x) {
	mi[x][0] = ma[x][0] = t[x].a[0];
	mi[x][1] = ma[x][1] = t[x].a[1];
	mf[x] = f[x];
	if(ch[x][0]) {
		mf[x] += mf[ch[x][0]];
		for(int i = 0; i < 2; i ++)
			mi[x][i] = min(mi[x][i], mi[ch[x][0]][i]),
			ma[x][i] = max(ma[x][i], ma[ch[x][0]][i]);
	}
	if(ch[x][1]) {
		mf[x] += mf[ch[x][1]];
		for(int i = 0; i < 2; i ++)
			mi[x][i] = min(mi[x][i], mi[ch[x][1]][i]),
			ma[x][i] = max(ma[x][i], ma[ch[x][1]][i]);
	}
	size[x] = size[ch[x][0]] + size[ch[x][1]] + 1;
}
void dfs(int x) {
	if(!x) return;
	dfs(ch[x][0]);
	ls[++ sz] = x;
	dfs(ch[x][1]);
}
int cmpp(int x, int y) {
	return t[x].a[dd] < t[y].a[dd];
}
int build(int l, int r, int o) {
	if(l > r) return 0;
	int mid = (l + r) >> 1;
	dd = o;
	nth_element(ls + l, ls + mid, ls + r + 1, cmpp);
	int rt = ls[mid];
	d[rt] = o;
	ch[rt][0] = build(l, mid - 1, (o + 1) % 2);
	ch[rt][1] = build(mid + 1, r, (o + 1) % 2);
	update(rt);
	return rt;
}
int ans = 0;
void query(int x, int a, int b, int c) {
	int tt = 0;
	tt += ma[x][0] * a + ma[x][1] * b < c;
	tt += ma[x][0] * a + mi[x][1] * b < c;
	tt += mi[x][0] * a + ma[x][1] * b < c;
	tt += mi[x][0] * a + mi[x][1] * b < c;
	if(tt == 4) {
		ans += mf[x];
		return;
	}
	if(tt == 0) return;
	if(a * t[x].a[0] + b * t[x].a[1] < c) ans += f[x];
	
	query(ch[x][0], a, b, c), query(ch[x][1], a, b, c);
}
signed main() {
	srand(time(NULL));
	scanf("%lld%lld", &n, &m);
	for(int i = 1; i <= n; i ++) {
		++ tot;
		scanf("%lld%lld%lld", &t[tot].a[0], &t[tot].a[1], &f[tot]);
		ls[++ sz] = tot;
	}
	root = build(1, sz, 0);
	for(int i = 1; i <= m; i ++) {
		ans = 0;
		int a, b, c;
		scanf("%lld%lld%lld", &a, &b, &c);
		query(root, a, b, c);
		printf("%lld\n", ans);	
	}		
	
	return 0;
}

P2093 [国家集训队]JZPFAR

这题和第一题有什么区别???

code:


#include
#define N 1000005
#define int long long
using namespace std;
struct A {
	int x, y, id;
} a[N];
int cmp1(A x, A y) {
	return x.x < y.x;
}
int cmp2(A x, A y) {
	return x.y < y.y;
}
int L[N], R[N], D[N], U[N], ch[N][2], n, k, X, Y, m;
void update(int x) {
	L[x] = R[x] = a[x].x;
	D[x] = U[x] = a[x].y;
	if(ch[x][0]) {
		L[x] = min(L[x], L[ch[x][0]]), R[x] = max(R[x], R[ch[x][0]]);
		D[x] = min(D[x], D[ch[x][0]]), U[x] = max(U[x], U[ch[x][0]]);	
	} 
	if(ch[x][1]) {
		L[x] = min(L[x], L[ch[x][1]]), R[x] = max(R[x], R[ch[x][1]]);
		D[x] = min(D[x], D[ch[x][1]]), U[x] = max(U[x], U[ch[x][1]]);	
	} 
}
int build(int l, int r, int o) { 
	if(l > r) return 0;//printf("  %d %d\n", l, r);
	int mid = (l + r) >> 1;
	if(!o) nth_element(a + l, a + mid, a + r + 1, cmp1);
	else nth_element(a + l, a + mid, a + r + 1, cmp2);
	ch[mid][0] = build(l, mid - 1, o ^ 1);
	ch[mid][1] = build(mid + 1, r, o ^ 1);
	update(mid);
	return mid;
}
int pf(int x) { return x * x;}
int getmax(int y) {
	return max(pf(X - L[y]), pf(X - R[y])) + max(pf(Y - D[y]), pf(Y - U[y]));
}
struct AA {
	int dis, id;
};
bool operator < (AA x, AA y) {
	if(x.dis != y.dis) return x.dis > y.dis;
	return x.id < y.id;
}
priority_queue<AA> q;
void query(int x) {
	if(!x) return;
	int t = pf(a[x].x - X) + pf(a[x].y - Y); //printf("  %d  (%d)\n", x, t);
	if(t > q.top().dis || (q.top().dis == t && a[x].id < q.top().id)) q.pop(), q.push(AA{t, a[x].id});//, printf("*%d*", t);;
	int disl = getmax(ch[x][0]), disr = getmax(ch[x][1]);
	if(disl > disr) {
		if(disl >= q.top().dis) query(ch[x][0]);
		if(disr >= q.top().dis) query(ch[x][1]);
	} else {
		if(disr >= q.top().dis) query(ch[x][1]);
		if(disl >= q.top().dis) query(ch[x][0]);
	}
//	query(ch[x][0]), query(ch[x][1]);
}
signed main() {
	scanf("%lld", &n);
	for(int i = 1; i <= n; i ++) scanf("%lld%lld", &a[i].x, &a[i].y), a[i].id = i;
	int root = build(1, n, 0);
	scanf("%lld", &m);
	for(int i = 1; i <= m; i ++) {
		scanf("%lld%lld%lld", &X, &Y, &k);
		while(q.size()) q.pop();
		for(int j = 1; j <= k; j ++) q.push(AA{-1, 0});
		query(root);
		printf("%lld\n", q.top().id);
	}
	return 0;
}

P4849 寻找宝藏

和四维偏序那题差不多
多维护一个东西

code:


#include
#define N 1000005
#define ll long long
#define mod 998244353
using namespace std;
ll mi[N][5], ma[N][5], mf[N], f[N];
int sz, ch[N][2], size[N], ls[N], tot, dd, root, d[N], ms[N], gs[N];
struct T {
	int a[5];
} t[N];
inline void MOD(int &x) {
	if(x >= mod) x -= mod;
}
void update(int x) {
	mi[x][0] = ma[x][0] = t[x].a[0];
	mi[x][1] = ma[x][1] = t[x].a[1];
	mi[x][2] = ma[x][2] = t[x].a[2];
	mf[x] = f[x];
	ms[x] = gs[x];
	if(ch[x][0]) {
		if(mf[ch[x][0]] == mf[x]) {
			ms[x] += ms[ch[x][0]];
			MOD(ms[x]);
		} else 
		if(mf[ch[x][0]] > mf[x]) {
			mf[x] = mf[ch[x][0]];
			ms[x] = ms[ch[x][0]];
		}
		for(int i = 0; i < 3; i ++)
			mi[x][i] = min(mi[x][i], mi[ch[x][0]][i]),
			ma[x][i] = max(ma[x][i], ma[ch[x][0]][i]);
	}
	if(ch[x][1]) {
		if(mf[ch[x][1]] == mf[x]) {
			ms[x] += ms[ch[x][1]];
			MOD(ms[x]);
		} else 
		if(mf[ch[x][1]] > mf[x]) {
			mf[x] = mf[ch[x][1]];
			ms[x] = ms[ch[x][1]];
		}
		for(int i = 0; i < 3; i ++)
			mi[x][i] = min(mi[x][i], mi[ch[x][1]][i]),
			ma[x][i] = max(ma[x][i], ma[ch[x][1]][i]);
	}
	size[x] = size[ch[x][0]] + size[ch[x][1]] + 1;
}
void dfs(int x) {
	if(!x) return;
	dfs(ch[x][0]);
	ls[++ sz] = x;
	dfs(ch[x][1]);
}
int cmpp(int x, int y) {
	return t[x].a[dd] < t[y].a[dd];
}
int build(int l, int r, int o) {
	if(l > r) return 0;
	int mid = (l + r) >> 1;
	dd = o;
	nth_element(ls + l, ls + mid, ls + r + 1, cmpp);
	int rt = ls[mid];
	d[rt] = o;
	ch[rt][0] = build(l, mid - 1, (o + 1) % 3);
	ch[rt][1] = build(mid + 1, r, (o + 1) % 3);
	update(rt);
	return rt;
}
void rebuild(int &x) {
	sz = 0;
	dfs(x);
	x = build(1, sz, 0);
}
const double alpha = 0.725;
bool bad(int x) {return alpha * size[x] <= (double)max(size[ch[x][0]], size[ch[x][1]]);}
void insert(int &x, int y) {
	if(!x) {
		x = y;
		update(x);
		return;
	}
	if(t[y].a[d[x]] <= t[x].a[d[x]]) insert(ch[x][0], y);
	else insert(ch[x][1], y);
	update(x);
	if(bad(x)) rebuild(x);
}
ll ans = 0;
int anss = 0;
void query(int x, int y) { 
	if(!x || t[y].a[0] < mi[x][0] || t[y].a[1] < mi[x][1] || t[y].a[2] < mi[x][2]) return ; 
	if(ma[x][0] <= t[y].a[0] && ma[x][1] <= t[y].a[1] && ma[x][2] <= t[y].a[2]) {
		if(mf[x] == ans) anss += ms[x], MOD(anss);
		else
		if(mf[x] > ans) ans = mf[x], anss = ms[x];
		return;
	}
	if(t[x].a[0] <= t[y].a[0] && t[x].a[1] <= t[y].a[1] && t[x].a[2] <= t[y].a[2]) {
		if(f[x] == ans) anss += gs[x], MOD(anss); 
		else 
		if(f[x] > ans) ans = f[x], anss = gs[x];
	}
	if(mf[x] < ans) return;
	query(ch[x][0], y), query(ch[x][1], y);
}
int n, m;
struct A {
	int a[5]; int val;
} a[N];
int cmp(A x, A y) {
	if(x.a[0] != y.a[0]) return x.a[0] < y.a[0];
	if(x.a[1] != y.a[1]) return x.a[1] < y.a[1];
	if(x.a[2] != y.a[2]) return x.a[2] < y.a[2];
	return x.a[3] < y.a[3];
}
int main() {
	srand(time(NULL));
	scanf("%d%d", &n, &m);
	for(int i = 1; i <= n; i ++) scanf("%d%d%d%d%d", &a[i].a[0], &a[i].a[1], &a[i].a[2], &a[i].a[3], &a[i].val);
	sort(a + 1, a + 1 + n, cmp);
 	for(int i = 1; i <= n; i ++) {
 		t[++ tot] = T{a[i].a[1], a[i].a[2], a[i].a[3]};
 		ans = 0, anss = 1;
 		query(root, tot);
		f[tot] = ans + a[i].val;
		gs[tot] = anss;
 		insert(root, tot);
	}
		
	ans = 0, anss = 0;
	for(int i = 1; i <= n; i ++) {
		if(ans == f[i]) anss += gs[i], MOD(anss);
		else if(f[i] > ans) ans = f[i], anss = gs[i];
	}
	printf("%lld\n%d", ans, anss);
	return 0;
}

luogu P2479 [SDOI2010]捉迷藏

维护最大最小就好了

code:


#include
#define N 1000005
#define ll long long
using namespace std;
struct A {
	int x, y, id;
} a[N];
int cmp1(A x, A y) {
	return x.x < y.x;
}
int cmp2(A x, A y) {
	return x.y < y.y;
}
int L[N], R[N], D[N], U[N], ch[N][2], n, k, X, Y, m;
void update(int x) {
	L[x] = R[x] = a[x].x;
	D[x] = U[x] = a[x].y;
	if(ch[x][0]) {
		L[x] = min(L[x], L[ch[x][0]]), R[x] = max(R[x], R[ch[x][0]]);
		D[x] = min(D[x], D[ch[x][0]]), U[x] = max(U[x], U[ch[x][0]]);	
	} 
	if(ch[x][1]) {
		L[x] = min(L[x], L[ch[x][1]]), R[x] = max(R[x], R[ch[x][1]]);
		D[x] = min(D[x], D[ch[x][1]]), U[x] = max(U[x], U[ch[x][1]]);	
	} 
}
int build(int l, int r, int o) { 
	if(l > r) return 0;
	int mid = (l + r) >> 1;
	if(!o) nth_element(a + l, a + mid, a + r + 1, cmp1);
	else nth_element(a + l, a + mid, a + r + 1, cmp2);
	ch[mid][0] = build(l, mid - 1, o ^ 1);
	ch[mid][1] = build(mid + 1, r, o ^ 1);
	update(mid);
	return mid;
}
int ans, anss;
int getmin(int y) {
	return max(X - R[y], 0) + max(L[y] - X, 0) + max(Y - U[y], 0) + max(D[y] - Y, 0);
}
int getmax(int y) {
	return max(abs(X - L[y]), abs(X - R[y])) + max(abs(Y - D[y]), abs(Y - U[y]));
}
void querymax(int x) { 
	if(!x) return;
	int t = abs(a[x].x - X) + abs(a[x].y - Y); 
	ans = max(ans, t);
	int disl = getmax(ch[x][0]), disr = getmax(ch[x][1]);
	if(disl > disr) {
		if(disl > ans) querymax(ch[x][0]);
		if(disr > ans) querymax(ch[x][1]);
	} else {
		if(disr > ans) querymax(ch[x][1]);
		if(disl > ans) querymax(ch[x][0]);
	}
}
void querymin(int x) { 
	if(!x) return;
	int t = abs(a[x].x - X) + abs(a[x].y - Y); 
	if(t) anss = min(t, anss);
	int disl = getmin(ch[x][0]), disr = getmin(ch[x][1]);
	if(disl < disr) {
		if(disl < anss) querymin(ch[x][0]);
		if(disr < anss) querymin(ch[x][1]);
	} else {
		if(disr < anss) querymin(ch[x][1]);
		if(disl < anss) querymin(ch[x][0]);
	}
}
int main() {
	scanf("%d", &n);
	for(int i = 1; i <= n; i ++) scanf("%d%d", &a[i].x, &a[i].y), a[i].id = i;
	int root = build(1, n, 0), ANS = 2147483647;
	for(int i = 1; i <= n; i ++) {
		X = a[i].x, Y = a[i].y;
		ans = 0, anss = 2147483647;
		querymax(root), querymin(root);
		ANS = min(ANS, ans - anss);
	}
	printf("%d", ANS);
	return 0;
}

P4148 简单题

单点修改,矩阵求和
其实这个才是板子题

不过前面也讲了,不平衡直接拍扁重建就好了

code:

#include
#define N 1000005
using namespace std;
int mi[N][5], ma[N][5], sum[N], sz, f[N], ch[N][2], size[N], ls[N], tot, dd, root, d[N];
struct T {
	int a[5];
} t[N];
void update(int x) {
	mi[x][0] = ma[x][0] = t[x].a[0];
	mi[x][1] = ma[x][1] = t[x].a[1];
	sum[x] = f[x];
	if(ch[x][0]) {
		sum[x] += sum[ch[x][0]];
		for(int i = 0; i < 3; i ++)
			mi[x][i] = min(mi[x][i], mi[ch[x][0]][i]),
			ma[x][i] = max(ma[x][i], ma[ch[x][0]][i]);
	}
	if(ch[x][1]) {
		sum[x] += sum[ch[x][1]];
		for(int i = 0; i < 3; i ++)
			mi[x][i] = min(mi[x][i], mi[ch[x][1]][i]),
			ma[x][i] = max(ma[x][i], ma[ch[x][1]][i]);
	}
	size[x] = size[ch[x][0]] + size[ch[x][1]] + 1;
}
void dfs(int x) {
	if(!x) return;
	dfs(ch[x][0]);
	ls[++ sz] = x;
	dfs(ch[x][1]);
}
int cmpp(int x, int y) {
	return t[x].a[dd] < t[y].a[dd];
}
int build(int l, int r, int o) {
	if(l > r) return 0;
	int mid = (l + r) >> 1;
	dd = o;
	nth_element(ls + l, ls + mid, ls + r + 1, cmpp);
	int rt = ls[mid];
	d[rt] = o;
	ch[rt][0] = build(l, mid - 1, o ^ 1);
	ch[rt][1] = build(mid + 1, r, o ^ 1);
	update(rt);
	return rt;
}
void rebuild(int &x) {
	sz = 0;
	dfs(x);
	x = build(1, sz, 0);
}
const double alpha = 0.765;
bool bad(int x) {return alpha * size[x] <= (double)max(size[ch[x][0]], size[ch[x][1]]);}
void insert(int &x, int y) {
	if(!x) {
		x = y;
		update(x);
		return;
	}
	if(t[y].a[d[x]] <= t[x].a[d[x]]) insert(ch[x][0], y);
	else insert(ch[x][1], y);
	update(x);
	if(bad(x)) rebuild(x);
}
int lastans = 0, X, Y, XX, YY;
void query(int x) {
	if(!x || ma[x][0] < X || mi[x][0] > XX || ma[x][1] < Y || mi[x][1] > YY) return ; 
	if(X <= mi[x][0] && ma[x][0] <= XX && Y <= mi[x][1] && ma[x][1] <= YY) {lastans += sum[x]; return;}
	if(X <= t[x].a[0] && t[x].a[0] <= XX && Y <= t[x].a[1] && t[x].a[1] <= YY) lastans += f[x]; 
	query(ch[x][0]), query(ch[x][1]);
}
int n;
int main() {
	scanf("%d", &n);
	while(1) {
		int opt;
		scanf("%d", &opt);
		if(opt == 3) return 0;
		if(opt == 1) {
			++ tot;
			scanf("%d%d%d", &t[tot].a[0], &t[tot].a[1], &f[tot]);
			t[tot].a[0] ^= lastans, t[tot].a[1] ^= lastans, f[tot] ^= lastans;
			insert(root, tot);
		} else {
			scanf("%d%d%d%d", &X, &Y, &XX, &YY);
			X ^= lastans, Y ^= lastans, XX ^= lastans, YY ^= lastans;
			lastans = 0;
			query(root);
			printf("%d\n", lastans);
		}
	}
	return 0;
}

然后就没了,真是一个神奇的数据结构啊!!!
啊啊啊♂
当然树套树和cdq嵌套也是必须要掌握的呢

你可能感兴趣的:(K-Dtree)