P3833 [SHOI2012]魔法树

放在前面的前面:本文有树剖图解

放在前面:一道树剖板子题,关于树剖的教程网上数不胜数,我就只 概述 一下,就不造轮子了,具体细节请见 OI WIKI

一. 过程概述

0. 基础:邻接表,线段树。

1. 第一次 dfs:

求出该有根树(如果题目没明确根就任选一个)所有节点的父节点,深度,(以他为根的)子树的大小, 重儿子。

2. 第二次 dfs:

求出该有根树所有节点的 dfs 序—— dfsdfs,每个 dfs 对应的节点编号—— dfsidxdfsidxi=dfsidxdfsii=dfsidx_{dfs_i}),所在重链的链顶—— toptop,其子树中节点中 dfs 序最大的一个—— bottombottom。用途:

dfsdfs:若这棵树上本来就有权值需要用它辅助给线段树建树。本题中无用。

dfsidxdfsidx:用他将线段树上节点的编号转化为线段树上节点的编号。

toptop:进行树链上操作时需要。(详见我的代码和 OI WIKI

bottombottom:进行子树上操作时需要。(详见我的代码和 OI WIKI

3. 权值增减与查询:

详解见 OI WIKI,这里只放两张图:

树和dfs序:

(madeby:大佬 EternalAlexander 的 OI Painter

另一张dfs图:

有点先序遍历内味了,只不过不是根左右,而是根重轻。
由此也可发现:树上一条重链或一棵子树都是线段树上连续的一部分,这正是树剖的核心。

二. 代码

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
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
#include<bits/stdc++.h>
using namespace std;
#define re register
typedef long long LL;
const int MAXn = 1e5;
const int MAXm = MAXn - 1;

template <class T>
inline void read(T &x) {
register char c;while (c = getchar(), c < '0' || c >'9');register T s(c - '0');while (c = getchar(), c >= '0' && c <= '9') {s = (s << 1) + (s << 3) + c - '0';}x = s;
}

int cntnext, head[MAXn + 10], nex[MAXm + 10], to[MAXm + 10];//////
inline void Insert(int from, int too) { //
nex[++cntnext] = head[from]; //邻接表
head[from] = cntnext; //
to[cntnext] = too; //////
}

struct Node { //////线段树
LL sum; //
int l; //
int r; //
LL add; //
}; //
Node stt[MAXn * 4 + 10]; //
void _BuildUseArray_(int nodeid, int l, int r, LL *array) { //
stt[nodeid].l = l; //
stt[nodeid].r = r; //
if (l == r) { //
stt[nodeid].sum = array[l]; //
return; //
} //
int mid = (l + r) >> 1; //
_BuildUseArray_((nodeid << 1), l, mid, array); //
_BuildUseArray_((nodeid << 1) + 1, mid + 1, r, array); //
stt[nodeid].sum = (stt[(nodeid << 1)].sum + stt[(nodeid << 1) + 1].sum);
} //
void _Build0_(int nodeid, int l, int r) { //
stt[nodeid].l = l; //
stt[nodeid].r = r; //
if (l == r) { //
return; //
} //
int mid = (l + r) >> 1; //
_Build0_((nodeid << 1), l, mid); //
_Build0_((nodeid << 1) + 1, mid + 1, r); //
} //
void _Spread_(int nodeid) { //
stt[(nodeid << 1)].sum = ( stt[(nodeid << 1)].sum + ((stt[(nodeid << 1)].r - stt[(nodeid << 1)].l + 1) * stt[nodeid].add) );
stt[(nodeid << 1) + 1].sum = ( stt[(nodeid << 1) + 1].sum + (stt[nodeid].add * (stt[(nodeid << 1) + 1].r - stt[(nodeid << 1) + 1].l + 1)) );
//
stt[(nodeid << 1)].add = (stt[(nodeid << 1)].add + stt[nodeid].add);
stt[(nodeid << 1) + 1].add = (stt[(nodeid << 1) + 1].add + stt[nodeid].add);
//
stt[nodeid].add = 0; ///
} ////////// 线段树
void _Add_(int nodeid, int l, int r, LL k) { ///
if (stt[nodeid].l >= l && stt[nodeid].r <= r) { //
stt[nodeid].add = (stt[nodeid].add + k); //
stt[nodeid].sum = (stt[nodeid].sum + k * (stt[nodeid].r - stt[nodeid].l + 1));
return; //
} //
_Spread_(nodeid); //
int mid = (stt[nodeid].l + stt[nodeid].r) >> 1; //
if (l <= mid) _Add_((nodeid << 1), l, r, k); //
if (mid < r) _Add_((nodeid << 1) + 1, l, r, k); //
stt[nodeid].sum = (stt[(nodeid << 1)].sum + stt[(nodeid << 1) + 1].sum);
} //
LL _Eva_(int nodeid, int l, int r) { //
if (stt[nodeid].l >= l && stt[nodeid].r <= r) //
return stt[nodeid].sum; //
_Spread_(nodeid); //
LL val = 0; //
int mid = (stt[nodeid].l + stt[nodeid].r) >> 1; //
if (l <= mid) val = (val + _Eva_((nodeid << 1), l, r)); //
if (mid < r) val = (val + _Eva_((nodeid << 1) + 1, l, r));
return val; //
} //
void BuildUseArray(int l, int r, LL *array) { //
_BuildUseArray_(1, l, r, array); //
} //
void Build0(int l, int r) { //
memset(stt, 0, sizeof(stt)); //
_Build0_(1, l, r); //
} //
void Add(int l, int r, LL k) { //
_Add_(1, l, r, k); //
} //
LL Eva(int l, int r) { //
return _Eva_(1, l, r); //
} //////线段树

int fa[MAXn + 10], dep[MAXn + 10], siz[MAXn + 10], hson[MAXn + 10];//树剖
int Dfs1(int nodeid, int fat, int deep) { ////
int size = 1; ///
int maxsiz = 0; //
int maxer = 0; //
int eachsiz; //
fa[nodeid] = fat; //
dep[nodeid] = deep; //
for (re int i = head[nodeid]; i; i = nex[i]) { //
eachsiz = Dfs1(to[i], nodeid, deep + 1); //
size += eachsiz; //
if (eachsiz > maxsiz) { //
maxsiz = eachsiz; //
maxer = to[i]; //
} //
} //
siz[nodeid] = size; //
hson[nodeid] = maxer; //
return size; //
} //
//
int cntdfs, dfs[MAXn + 10], dfsidx[MAXn + 10], top[MAXn + 10], bottom[MAXn + 10];
void Dfs2(int nodeid, int topp) { //
dfs[++cntdfs] = nodeid; //
dfsidx[nodeid] = cntdfs; //
top[nodeid] = topp; //
if (hson[nodeid]) { //
Dfs2(hson[nodeid], topp); //
for (int i = head[nodeid]; i; i = nex[i]) { //
if (to[i] != fa[nodeid] && to[i] != hson[nodeid]) { //
Dfs2(to[i], to[i]); //
} //
} //
} //
bottom[nodeid] = dfs[cntdfs]; //
} ///
////////// 树剖
void TreePathAdd(int x, int y, int k) { ///
while (top[x] != top[y]) { //
if (dep[top[x]] > dep[top[y]]) { //
swap(x, y); //
} //
Add(dfsidx[top[y]], dfsidx[y], k); //
y = fa[top[y]]; //
} //
if (dep[x] > dep[y]) { //
swap(x, y); //
} //
Add(dfsidx[x], dfsidx[y], k); //
} //
//
LL TreePathEva(int x, int y) { //
LL sum = 0; //
while (top[x] != top[y]) { //
if (dep[top[x]] > dep[top[y]]) { //
swap(x, y); //
} //
sum += Eva(dfsidx[top[y]], dfsidx[y]); //
y = fa[top[y]]; //
} //
if (dep[x] > dep[y]) { //
swap(x, y); //
} //
sum += Eva(dfsidx[x], dfsidx[y]); //
return sum; //
} //
//
void SonTreeAdd(int rootid, int k) { //
Add(dfsidx[rootid], dfsidx[bottom[rootid]], k); //
} //
//
LL SonTreeEva(int rootid) { //
return Eva(dfsidx[rootid], dfsidx[bottom[rootid]]); //
} //////树剖

int n, q;
int main() {
read(n);
Build0(1, n);
for (re int i = 1, from, to; i < n; ++i) {
read(from), read(to);
Insert(from + 1, to + 1);
}
Dfs1(1, 0, 1);
Dfs2(1, 1);
read(q);
for (re int i = 1, from, to, val, opt, root; i <= q; ++i) {
getchar();
opt = getchar();
switch (opt) {
case 'A':
read(from), read(to), read(val);
TreePathAdd(from + 1, to + 1, val);
break;
case 'Q':
read(root);
printf("%lld\n", SonTreeEva(root + 1));
break;
}
}
}