zqs`s blog

有朋自远方来,虽远必诛

nkoj P5668题解

$\texttt{Description}$

给定一棵大小为 n 的有根点权树,支持以下操作:

 - 换根
  - 修改点权

  • 查询子树最小值

$\texttt{Data Range:}1\le n\le 10^5$

$\texttt{Solution}$

LCT是什么啊,LCT的前置知识树链剖分又是什么啊,LCT的前置知识Splay我也不会写啊,得出我菜得没救。

233能换根的数据结构看来只有Splay和LCT两个我不会的了啊/kk

由于老板放到了dfs序作业里面,所以就往这方面想。

预处理出dfs序。考虑换根操作。老板评讲的时候说了“换根会导致整棵树的形态发生变化,极其耗时,因此换根只在我们脑海中进行,程序中不可能真的换根”。

由于只有换根操作改变了树的形态,因此记录下当前的根 $root$。

对于当前询问以 $x$ 为根的子树的最小值,分类讨论(我们按照 $1$ 为根建好树,之后所有的“父亲”“儿子”“祖先”定义都是在这颗建好的树上的):

  1. $x$ 就是 $root$。输出所有节点中最小权值即可。

  2. $x$ 不是 $root$ 的祖先。那么你 $root$ 当了老大关我p事雨我无瓜,直接输出原来的树上以 $x$ 为根的子树最小值。

  3. $x$ 是 $root$ 的祖先。那么设 $x$ 的儿子 $y$ 是 $root$ 的祖先(特别的,$y$ 可能等于 $root$。那么在区间 $[1,In_y),(Out_y,n]$ 中取最小值。

修改点权可以线段树,判断 $x$ 是否是 $root$ 祖先以及求 $y$ 可以倍增LCA。

$\texttt{Code}$

底部附hack数据方便调试。

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
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
#include <cstdio>
#include <cmath>
#include <vector>

inline int min(const int x, const int y) {return x < y ? x : y;}
int fa[100005][20], Dep[100005], In[100005], Out[100005], val[100005], n, cnt;
std::vector<int> sons[100005];

struct Segment_tree {
struct Node {
int l, r, v;
} c[400005];
void make_tree(const int O, const int L, const int R) {
c[O].l = L, c[O].r = R;
if (L != R) make_tree(O << 1, L, L + R >> 1), make_tree(O << 1 | 1, (L + R >> 1) + 1, R);
}
void update(const int O, const int p, const int x) {
if (c[O].l == c[O].r) {c[O].v = x; return;}
if (p <= c[O << 1].r) update(O << 1, p, x);
else update(O << 1 | 1, p, x);
c[O].v = min(c[O << 1].v, c[O << 1 | 1].v);
}
int query(const int O, const int l, const int r) const {
if (l > r) return 0x3fffffff;
if (l <= c[O].l && c[O].r <= r) return c[O].v;
const int mid(c[O << 1].r);
int ans(0x3fffffff);
if (l <= mid) ans = min(ans, query(O << 1, l, r));
if (mid < r) ans = min(ans, query(O << 1 | 1, l, r));
return ans;
}
} Tree;

inline void dfs(int u) {
Tree.update(1, In[u] = ++ cnt, val[u]);
int MAX(ceil(log2(Dep[u] = Dep[fa[u][0]] + 1)));
for (int i(1); i <= MAX; ++ i) fa[u][i] = fa[fa[u][i - 1]][i - 1];
for (auto i : sons[u]) if (i != fa[u][0]) dfs(i);
Out[u] = cnt;
}

inline int LCA(int x, int y) {
if (Dep[x] < Dep[y]) {int t(x); x = y, y = t;}
int MAX(ceil(log2(n)));
for (int i(0); i <= MAX; ++ i) if (Dep[x] - Dep[y] & 1 << i) x = fa[x][i];
if (x == y) return x;
for (int i(MAX); i >= 0; -- i)
if (fa[x][i] != fa[y][i]) x = fa[x][i], y = fa[y][i];
return fa[x][0];
}
inline int Jump(int x, const int y) {
int MAX(ceil(log2(n)));
for (int i(0); i <= MAX; ++ i) if (Dep[x] - Dep[y] - 1 & 1 << i) x = fa[x][i];
return x;
}

int main() {
int m, root(1);
scanf("%d%d", &n, &m);
Tree.make_tree(1, 1, n);
for (int i(1); i <= n; ++ i) {
scanf("%d%d", &fa[i][0], val + i);
if (fa[i][0]) sons[fa[i][0]].push_back(i);
}
dfs(1);
while (m --) {
char op;
int x;
scanf(" %c%d", &op, &x);
if (op == 'V') {
int y;
scanf("%d", &y);
Tree.update(1, In[x], y);
} else if (op == 'E') root = x;
else {
if (x == root) {printf("%d\n", Tree.query(1, 1, n)); continue;}
const int lca(LCA(x, root));
if (lca != x) printf("%d\n", Tree.query(1, In[x], Out[x]));
else {
int y(Jump(root, x));
printf("%d\n", min(Tree.query(1, 1, In[y] - 1), Tree.query(1, Out[y] + 1, n)));
}
}
}
return 0;
}/*
10 2
0 6
1 7
2 1
2 4
4 2
1 10
6 5
7 6
7 0
6 18
E 7
Q 6
*/
-------------本文结束感谢您的阅读-------------