k-d tree

简介

算是一种冷门数据结构了

应用

支持矩阵加单点操作

静态建树需要提前知道有哪些点,复杂度为 $O(n\sqrt n)$

动态插入的话复杂度为 $O(n\sqrt {n\log n})$

模板

静态建树,这里采用的是线段树式的建法,所以每个点实际上是下放到叶子节点的

结构体中存储的 $p$ 只是用来在查找单点的时候指路

需要注意 $cmp$ 一定要将所有维度都指定顺序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
struct Point {
int x, y;

friend bool operator == (const Point &u, const Point &v) { return u.x == v.x && u.y == v.y; }
} a[maxn];
inline bool cmpx(const Point &u, const Point &v) {
if (u.x == v.x) return u.y < v.y;
return u.x < v.x;
}

inline bool cmpy(const Point &u, const Point &v) {
if (u.y == v.y) return u.x < v.x;
return u.y < v.y;
}

vector<bool(*)(const Point &u, const Point &v)> cmp;
void init_cmp() {
cmp.push_back(cmpx);
cmp.push_back(cmpy);
}

#define lc i << 1
#define rc i << 1 | 1
struct KDtree {
int xl, xr, yl, yr, v, tag;
Point p;
} T[maxn * 4];
inline void maintain(int i) {
T[i].xl = min(T[lc].xl, T[rc].xl);
T[i].xr = max(T[lc].xr, T[rc].xr);
T[i].yl = min(T[lc].yl, T[rc].yl);
T[i].yr = max(T[lc].yr, T[rc].yr);
}

inline void Update(int i, int v) {

}

inline void pushdown(int i) {
int &tag = T[i].tag; if (!tag) return ;
Update(lc, v); Update(rc, v);
tag = 0;
}

void build(int i, int l, int r, int d) {
T[i].tag = 0;
if (l == r) return T[i] = { a[l].x, a[l].x, a[l].y, a[l].y, 1, 0, a[l] }, void();
int m = l + r >> 1; nth_element(a + l, a + m, a + r + 1, cmp[d]); T[i].p = a[m];
build(lc, l, m, d ^ 1); build(rc, m + 1, r, d ^ 1);
maintain(i);
}

// 矩形
void update(int i, int xl, int xr, int yl, int yr, int v) {
if (max(T[i].xl, xl) > min(T[i].xr, xr) || max(T[i].yl, yl) > min(T[i].yr, yr)) return ;
if (xl <= T[i].xl && T[i].xr <= xr && yl <= T[i].yl && T[i].yr <= yr)
return Update(i, v);
pushdown(i);
update(lc, xl, xr, yl, yr, v); update(rc, xl, xr, yl, yr, v);
maintain(i);
}

// 单点
int query(int i, int l, int r, const Point &p, int d) {
if (l == r) return T[i].v;
int m = l + r >> 1; pushdown(i);
if (T[i].p == p || cmp[d](p, T[i].p))
return query(lc, l, m, p, d ^ 1);
else return query(rc, m + 1, r, p, d ^ 1);
}

动态插入大概就是先攒着,攒到一定数量再重新建树

1
if ((ar - al) * (ar - al) > ar * 40) build(1, 1, al = ar, 0);

例题

  1. 简要题意:给定一棵以 $1$ 为根的 $n$ 个点的有根树,现在有 $m$ 次操作,操作有两种,第一种操作给定 $u,k,c$,将 $u$ 的子树内距离 $u$ 不超过 $k$ 的点染成 $c$;第二种操作给定 $u$,求 $u$ 的颜色

    $n\le 10^5$

    简要题解:大概就是矩形赋值,单点查询,直接上 $KD~Tree$ 即可

    时间复杂度 $O(n\sqrt n)$

    bzoj 4154 [Ipsc2015]Generating Synergy

  2. 简要题意:给定一个 $n\times n$ 的网格图,初始时每个位置都为 $0$,现在有 $m$ 次操作,操作有两种,第一种操作给定 $(x,y)$ 和 $v$,将 $(x,y)$ 加上 $v$;第二种操作给定矩形的左上角和右下角 $(x_1,y_1)$ 和 $(x_2,y_2)$,求矩形和

    $n\le 5\times 10^5$,强制在线

    简要题解:单点加,矩阵求和

    我们将单点加看成插入,众所周知 $KD~~Tree$ 是不能插入的

    所以我们考虑一种根号分治,即插入超过 $O(\sqrt n)$ 个节点就进行重构,这样时间复杂度为 $O(n\sqrt n\log n)$

    我们可以将阙值微调到 $O(\sqrt {n\log n})$,这样的时间复杂度就是 $O(n\sqrt {n\log n})$

    Luogu P4148 简单题