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;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· 写一个简单的SQL生成工具
· AI 智能体引爆开源社区「GitHub 热点速览」
· C#/.NET/.NET Core技术前沿周刊 | 第 29 期(2025年3.1-3.9)