【题解】51nod-2004-终结之时

题目

http://www.51nod.com/onlineJudge/questionCode.html#!problemId=2004

题意

在支配树上实现一些持久化操作,详见题面。

题解

  • 支配树裸题 + 树链剖分裸题 + 持久化线段树裸题
  • 求若干个点到根的链上结点的并的原理和求虚树很相似,只需要考虑 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
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
#include <bits/stdc++.h>
using namespace std;
using LL = long long;
#define FOR(i, x, y) for (decay<decltype(y)>::type i = (x), _##i = (y); i < _##i; ++i)
#define FORD(i, x, y) for (decay<decltype(x)>::type i = (x), _##i = (y); i > _##i; --i)
#ifdef zerol
#define dbg(args...) do { cout << "\033[32;1m" << #args << " -> "; err(args); } while (0)
#else
#define dbg(...)
#endif
void err() { cout << "\033[39;0m" << endl; }
template<template<typename...> class T, typename t, typename... Args>
void err(T<t> a, Args... args) { for (auto x: a) cout << x << ' '; err(args...); }
template<typename T, typename... Args>
void err(T a, Args... args) { cout << a << ' '; err(args...); }
// -----------------------------------------------------------------------------
const int N = 5E4 + 100;

vector<int> G[N];

namespace tl{
vector<int> G[N], rG[N];
int fa[N], idx[N], clk, ridx[N];
int c[N], best[N], semi[N], idom[N];
void init(int n) {
clk = 0;
fill(c, c + n + 1, -1);
FOR (i, 1, n + 1) ::G[i].clear();
FOR (i, 1, n + 1) semi[i] = best[i] = i;
fill(idx, idx + n + 1, 0);
}
void dfs(int u) {
idx[u] = ++clk; ridx[clk] = u;
for (int& v: G[u]) if (!idx[v]) { fa[v] = u; dfs(v); }
}
int fix(int x) {
if (c[x] == -1) return x;
int &f = c[x], rt = fix(f);
if (idx[semi[best[x]]] > idx[semi[best[f]]]) best[x] = best[f];
return f = rt;
}
void go(int rt) {
dfs(rt);
FORD (i, clk, 1) {
int x = ridx[i], mn = clk + 1;
for (int& u: rG[x]) {
if (!idx[u]) continue;
fix(u); mn = min(mn, idx[semi[best[u]]]);
}
c[x] = fa[x];
::G[semi[x] = ridx[mn]].push_back(x);
x = ridx[i - 1];
for (int& u: ::G[x]) {
fix(u);
if (semi[best[u]] != x) idom[u] = best[u];
else idom[u] = x;
}
::G[x].clear();
}

FOR (i, 2, clk + 1) {
int u = ridx[i];
if (idom[u] != semi[u]) idom[u] = idom[idom[u]];
::G[idom[u]].push_back(u);
}
}
}

int fa[N], dep[N], idx[N], out[N], ridx[N];
namespace hld {
int sz[N], son[N], top[N], clk;
void predfs(int u, int d) {
dep[u] = d; sz[u] = 1;
int& maxs = son[u] = -1;
for (int& v: G[u]) {
if (v == fa[u]) continue;
fa[v] = u;
predfs(v, d + 1);
sz[u] += sz[v];
if (maxs == -1 || sz[v] > sz[maxs]) maxs = v;
}
}
void dfs(int u, int tp) {
top[u] = tp; idx[u] = ++clk; ridx[clk] = u;
if (son[u] != -1) dfs(son[u], tp);
for (int& v: G[u])
if (v != fa[u] && v != son[u]) dfs(v, v);
out[u] = clk;
}
template<typename T>
int go(int u, int v, T&& f = [](int, int) {}) {
int uu = top[u], vv = top[v];
while (uu != vv) {
if (dep[uu] < dep[vv]) { swap(uu, vv); swap(u, v); }
f(idx[uu], idx[u]);
u = fa[uu]; uu = top[u];
}
if (dep[u] < dep[v]) swap(u, v);
f(idx[v], idx[u]);
return v;
}
}

int now;
namespace tree {
#define lson l, (l + r) / 2
#define rson (l + r) / 2 + 1, r
extern struct P *null, *pit, pool[];
struct P {
P *ls, *rs; LL sum; int addv; int time;
P* check() {
if (now == time) return this;
if (pit == pool) assert(0);
P* t = new (pit++) P; *t = *this; t->time = now; return t;
}
P* _do_add(int v, int l, int r) {
addv += v; sum += 1LL * (r - l + 1) * v; return this;
}
P* do_add(int v, int l, int r) {
if (this == null) return this;
return check()->_do_add(v, l, r);
}
P* _down(int l, int r) {
if (addv) {
ls = ls->do_add(addv, lson);
rs = rs->do_add(addv, rson);
addv = 0;
}
return this;
}
P* up() { sum = ls->sum + rs->sum; return this; }
P* down(int l, int r) { return check()->_down(l, r); }
} pool[N * 60], *pit = pool, *null = new P{0, 0, 0, 0, 0};
LL query(P* o, int ql, int qr, int l, int r) {
if (ql > r || l > qr) return 0;
if (ql <= l && r <= qr) return o->sum;
o = o->down(l, r);
return query(o->ls, ql, qr, lson) + query(o->rs, ql, qr, rson);
}
P* update(P* o, int ql, int qr, int v, int l, int r) {
if (ql > r || l > qr) return o;
if (ql <= l && r <= qr) return o->do_add(v, l, r);
else {
o = o->down(l, r);
o->ls = update(o->ls, ql, qr, v, lson);
o->rs = update(o->rs, ql, qr, v, rson);
return o->up();
}
}
template<typename T>
P* build(int l, int r, const T& f) {
P* t = new (pit++) P;
if (l == r) { t->sum = f(l); t->ls = t->rs = null; return t; }
t->ls = build(lson, f); t->rs = build(rson, f);
return t->up();
}
}

int w[N];
tree::P* rt[N * 2], *pp[N * 2];

char s[100];
int main() {
#ifdef zerol
freopen("inin", "r", stdin);
#endif
tree::null->ls = tree::null->rs = tree::null;
int n, m; cin >> n >> m;
FOR (i, 1, n + 1) scanf("%d", &w[i]);
tl::init(n);
FOR (_, 0, m) {
int u, v; scanf("%d%d", &u, &v);
tl::G[u].push_back(v); tl::rG[v].push_back(u);
}
tl::go(1); hld::predfs(1, 1); hld::dfs(1, 1);
rt[now = 0] = tree::build(1, n, [&](int x){ return w[ridx[x]]; });
pp[now] = tree::pit;

int Qn; cin >> Qn;
while (Qn--) {
scanf("%s", s);
if (s[0] == 'R') {
int t; scanf("%d", &t);
now = max(0, now - t);
tree::pit = pp[now];
} else {
++now; rt[now] = rt[now - 1];
int tp; scanf("%d", &tp);
if (s[0] == 'C') {
int u, w; scanf("%d%d", &u, &w);
if (tp == 1)
rt[now] = tree::update(rt[now], idx[u], idx[u], w, 1, n);
else if (tp == 2)
rt[now] = tree::update(rt[now], idx[u], out[u], w, 1, n);
else if (tp == 3)
hld::go(1, u, [&](int l, int r){
rt[now] = tree::update(rt[now], l, r, w, 1, n);
});
else assert(0);
pp[now] = tree::pit;
} else if (s[0] == 'Q') {
auto upup = [&](int u) {
LL ret = 0;
hld::go(u, 1, [&](int l, int r) {
ret += tree::query(rt[now], l, r, 1, n);
});
return ret;
};
if (tp == 3) {
static int a[N];
LL ans = 0; int k; scanf("%d", &k);
FOR (i, 0, k) scanf("%d", &a[i]);
sort(a, a + k, [](const int& x, const int& y) { return idx[x] < idx[y]; });
FOR (i, 0, k) ans += upup(a[i]);
FOR (i, 1, k) ans -= upup(hld::go(a[i], a[i - 1], [](int, int){}));
printf("%lld\n", ans);
} else {
int u; scanf("%d", &u);
if (tp == 1)
printf("%lld\n", tree::query(rt[now], idx[u], out[u], 1, n));
else if (tp == 2)
printf("%lld\n", upup(u));
else assert(0);
}
tree::pit = pp[--now];
} else assert(0);
}
}
}