K-D Tree 二进制分组学习笔记

K-D Tree 的二进制分组:

(以下默认 2-D Tree,即下文中的 \(k\) 不是 K-D 中的K.)

维护一个 K-D Tree 的森林,各子树大小为 \(2^x\).

设当前元素数量为 \(x\),则 x&(1<<k) == 1 说明当前维护的森林里有大小为 \(2^k\) 的 K-D Tree.

每次 insert,把 lowbit(cnt) 之下的树全拍扁重建,\(n\) 次 insert 拍扁+重建的复杂度为

\[\begin{align*} & \sum \limits_{k=0}^{\lg n} \dfrac{n}{2^{k+1}}O(2^k + 2^k \lg 2^k) \\ = & \sum \limits_{k=0}^{\lg n} \dfrac{n}{2^{k+1}}O(2^k+k 2^k) \\ = & \sum \limits_{k=0}^{\lg n} O(nk) \\ = & O(n \lg^2n) \end{align*} \]

每次查询的复杂度为

\[\sum \limits_{k=0}^{\lg n} O(2^{k/2}) \]

也可以写成 \(T(n) = T(\dfrac{n}{2}) + O(\sqrt n)\),由主定理知 \(T(n) = O(\sqrt n)\).

所以单次查询复杂度不变,仍为 \(O(\sqrt n)\).

P4148 代码:

#include <iostream>
#include <functional>
#include <algorithm>
#include <cmath>
#include <cstdio>
#include <vector>
#define sd std::
#define UP(i, s, e) for(auto i=s; i<e; ++i)
constexpr int N = 5e5, M = 2e5;
constexpr int NODS = 1<<(int)sd ceil(sd log2(M));
namespace KDT{ // }{{{
struct Point{
	int x, y, val; 
	Point& operator=(Point b){ x = b.x, y = b.y, val = b.val; return *this; }
};
bool cmp_x(Point a, Point b){ return a.x < b.x; }
bool cmp_y(Point a, Point b){ return a.y < b.y; }
sd function<bool(Point, Point)> cmps[2] = { cmp_x, cmp_y };
struct Node: public Point{
	Node *ls, *rs;
	int xl, yl, xr, yr, sum;
} nil_, *nil = &nil_, bf[NODS];
int nodcnt=0;
Node *nnod(){ return &bf[nodcnt++]; }
void update(Node *root){
	root->xl = root->xr = root->x; root->xr++;
	root->yl = root->yr = root->y; root->yr++;
	root->sum = root->val;
	if(root->ls != nil){
		root->xl = sd min(root->xl, root->ls->xl);
		root->yl = sd min(root->yl, root->ls->yl);
		root->xr = sd max(root->xr, root->ls->xr);
		root->yr = sd max(root->yr, root->ls->yr);
		root->sum += root->ls->sum;
	}
	if(root->rs != nil){
		root->xl = sd min(root->xl, root->rs->xl);
		root->yl = sd min(root->yl, root->rs->yl);
		root->xr = sd max(root->xr, root->rs->xr);
		root->yr = sd max(root->yr, root->rs->yr);
		root->sum += root->rs->sum;
	}
}
void build(Node *root, int l, int r, Point *ia, int dim=0){
	if(r-l == 1){
		(Point&)*root = ia[l];
		root->ls = root->rs = nil;
		update(root);
		return;
	}
	int mid = (l+r)/2;
	sd nth_element(ia+l, ia+mid, ia+r, cmps[dim]);
	(Point&)*root = ia[mid];
	root->ls = nnod();
	build(root->ls, l, mid, ia, dim^1);
	if(mid<r-1){
		root->rs = nnod();
		build(root->rs, mid+1, r, ia, dim^1);
	} else root->rs = nil;
	update(root);
}
int querynode(Node *root, int xl, int xr, int yl, int yr){
	if(root == nil) return 0;
	if(root->xr <= xl || root->xl >= xr || root->yr <= yl || root->yl >= yr) return 0;
	if(root->xl >= xl && root->xr <= xr && root->yl >= yl && root->yr <= yr) return root->sum;
	int ans = 0;
	if(root->x >= xl && root->x < xr && root->y >= yl && root->y < yr) ans += root->val;
	if(root->ls != nil) ans += querynode(root->ls, xl, xr, yl, yr);
	if(root->rs != nil) ans += querynode(root->rs, xl, xr, yl, yr);
	return ans;
}
void pia(Node *root, sd vector<Point> &pts){
	pts.push_back((Point)*root);
	if(root->ls != nil) pia(root->ls, pts);
	if(root->rs != nil) pia(root->rs, pts);
}
struct Kdtree{
	int cnt;
	sd vector<Node*> forest;
	sd vector<Point> rebus; // rebuilds
	void init(){
		cnt=0;
		forest.clear();
		forest.push_back(nil);
	}
	void insert(Point p){
		rebus.clear();
		rebus.push_back(p);
		cnt++;
		int rebucnt = __builtin_ctz(cnt&-cnt);
		UP(i, 0, rebucnt){
			if((int)forest.size() <= i) forest.push_back(nil);
			if(forest[i] == nil) continue;
			pia(forest[i], rebus);
			forest[i] = nil;
		}
		if((int)forest.size() <= rebucnt) forest.push_back(nil);
		KDT::nodcnt = (1<<rebucnt)-1;
		forest[rebucnt] = nnod();
		build(forest[rebucnt], 0, rebus.size(), rebus.data());
	}
	int query(int xl, int xr, int yl, int yr){
		int ret = 0;
		for(Node *i:forest){
			ret += querynode(i, xl, xr, yl, yr);
		}
		return ret;
	}
};
} // {}}}
namespace m{ // }{{{
KDT::Kdtree kdt;
int in, lans=0;
void work(){
	sd cin >> in;
	while(1){
		int op;
		sd cin >> op;
		if(op == 1){
			int x, y, a;
			sd cin >> x >> y >> a;
			x ^= lans, y ^= lans, a ^= lans;
			KDT::Point p = {x, y, a};
			kdt.insert(p);
		} else if(op == 2){
			int xl, yl, xr, yr;
			sd cin >> xl >> yl >> xr >> yr;
			xl ^= lans, yl ^= lans, xr ^= lans, yr ^= lans;
			xr++, yr++;
			int ans = kdt.query(xl, xr, yl, yr);
			sd cout << ans << '\n';
			lans = ans;
		} else if(op == 3){
			break;
		}
	}
}
} // {}}}
int main(){
	sd ios::sync_with_stdio(false);
	m::work(); 
	return 0;
}
posted @ 2023-07-11 09:26  383494  阅读(54)  评论(0编辑  收藏  举报