「CF 757G」Can Bash Save the Day?-可持久化点分树

给出一个 $n$ 个节点的树,和一个长为 $n$ 的排列 $p$,要求支持:

  1. 求 $\sum\limits_{i = l} ^ r \mathrm{dis}(p_i, x)$
  2. $\mathrm{swap}(p_i, p_{i + 1})$

强制在线。

链接

CF 757G

题解

这个题很容易联系到 HNOI 2015 的开店,甚至可以说是开店的加强版。

先考虑一个经典问题:
标记一个点,询问一个点到所有标记点的距离和。

这显然直接用点分树做就可以了,而对于区间询问且强制在线,我们可以考虑将点分树可持久化。

先将原树转为二叉树,具体方法就是令 $u$ 的儿子为 $c_1, c_2, \cdots, c_n$,新建 $n - 2$ 个节点 $t_1, t_2, \cdots, t_{n - 2}$,连边 $u \rightarrow t_1, t_1 \rightarrow t_2, \cdots, t_{n - 3} \rightarrow t_{n - 2}, c_1 \rightarrow u, c_2 \rightarrow t_1, \cdots, c_{n} \rightarrow t_{n - 2}$。

这样就可以保证点分树上的度数 $\leq 3$,然后 Path Copy 就可以可持久化了。

按照 $p_i$ 的顺序建立可持久化点分树,询问就是两个前缀和之差,修改就是版本 $i$ 的点分树中删除 $p_i$ 再插入 $p_{i + 1}$。

时间复杂度 $O(n \log n)$,空间复杂度 $O(n \log n)$。

小心空间…

代码

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
230
231
232
233
234
235
236
237
238
239
/**
* Copyright (c) 2016-2018, xehoth
* All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* 「CF 757G」Can Bash Save the Day? 05-03-2018
* 可持久化点分树
* @author xehoth
*/
#include <cstdio>
#include <iostream>
#include <vector>
struct InputOutputStream {
enum { SIZE = 1 << 18 | 1 };
char ibuf[SIZE], *s, *t, obuf[SIZE], *oh;
InputOutputStream() : s(), t(), oh(obuf) {}
~InputOutputStream() { fwrite(obuf, 1, oh - obuf, stdout); }
inline char read() {
return (s == t) && (t = (s = ibuf) + fread(ibuf, 1, SIZE, stdin)),
s == t ? -1 : *s++;
}
template <typename T>
inline InputOutputStream &operator>>(T &x) {
static char c;
static bool iosig;
for (c = read(), iosig = false; !isdigit(c); c = read()) {
if (c == -1) return *this;
iosig |= c == '-';
}
for (x = 0; isdigit(c); c = read()) x = x * 10 + (c ^ '0');
iosig && (x = -x);
return *this;
}
inline void print(char c) {
(oh == obuf + SIZE) && (fwrite(obuf, 1, SIZE, stdout), oh = obuf);
*oh++ = c;
}
template <typename T>
inline void print(T x) {
static int buf[21], cnt;
if (x != 0) {
(x < 0) && (print('-'), x = -x);
for (cnt = 0; x; x /= 10) buf[++cnt] = x % 10 | 48;
while (cnt) print((char)buf[cnt--]);
} else {
print('0');
}
}
template <typename T>
inline InputOutputStream &operator<<(const T &x) {
print(x);
return *this;
}
} io;
const int MAXN = 200000 + 1;
const int MAXG = MAXN * 2 + 1;
const int MAX_LOG = 19;
int n, q;
struct Edge {
int v, w;
Edge(int v, int w) : v(v), w(w) {}
};
std::vector<Edge> edge[MAXN], g[MAXG];
int a[MAXN];
int tot;
inline void addEdge(int u, int v, int w) {
g[u].emplace_back(v, w);
g[v].emplace_back(u, w);
}
inline void binarize(int u, int pre) {
static std::vector<Edge *> now;
now.clear();
for (auto &p : edge[u])
if (p.v != pre) now.push_back(&p);
int o = u;
for (int i = 0; i < (int)now.size(); i++) {
addEdge(o, now[i]->v, now[i]->w);
if (now.size() - i > 2) {
addEdge(o, ++tot, 0);
o = tot;
}
}
for (auto &p : edge[u])
if (p.v != pre) binarize(p.v, u);
}
bool vis[MAXG];
int sz[MAXG], idx[MAXG][MAX_LOG], dep[MAXG];
long long dis[MAXG][MAX_LOG];
void dfsSize(int u, int pre) {
sz[u] = 1;
for (auto &p : g[u]) {
if (!vis[p.v] && p.v != pre) {
dfsSize(p.v, u);
sz[u] += sz[p.v];
}
}
}
int get(int u, int pre, int n) {
for (auto &p : g[u])
if (!vis[p.v] && p.v != pre && sz[p.v] > n) return get(p.v, u, n);
return u;
}
void dfs(int u, int pre, int d) {
for (auto &p : g[u]) {
if (!vis[p.v] && p.v != pre) {
idx[p.v][d] = idx[u][d];
dis[p.v][d] = dis[u][d] + p.w;
dfs(p.v, u, d);
}
}
}
void build(int u, int d) {
dfsSize(u, 0);
vis[u = get(u, 0, sz[u] / 2)] = true;
dep[u] = d;
int c = 0;
for (auto &p : g[u]) {
if (!vis[p.v]) {
dis[p.v][d] = p.w;
idx[p.v][d] = c++;
dfs(p.v, u, d);
build(p.v, d + 1);
}
}
}
char *cur;
struct Node *null;
struct Node {
static const int NODE_SIZE;
Node *c[3];
int sz;
long long sum;
inline void *operator new(size_t) { return cur += NODE_SIZE; }
};
const int Node::NODE_SIZE = sizeof(Node);
const int MAXM = MAXG * MAX_LOG * 2;
char pool[MAXM * Node::NODE_SIZE];
void insert(Node *&p, Node *pre, int u, int d) {
p = new Node(*pre);
p->sz++;
p->sum += dis[u][d] - dis[u][d - 1];
if (d != dep[u]) insert(p->c[idx[u][d]], pre->c[idx[u][d]], u, d + 1);
}
void erase(Node *&p, Node *pre, int u, int d) {
p = new Node(*pre);
p->sz--;
p->sum -= dis[u][d] - dis[u][d - 1];
if (d != dep[u]) erase(p->c[idx[u][d]], pre->c[idx[u][d]], u, d + 1);
}
long long query(Node *p, int u, int d) {
return ((d == dep[u] || p == null) ? 0 : query(p->c[idx[u][d]], u, d + 1)) +
p->sum + p->sz * (dis[u][d] - dis[u][d - 1]);
}
Node *root[MAXG];
int main() {
// freopen("sample/1.in", "r", stdin);
cur = pool;
null = (Node *)pool;
null->c[0] = null->c[1] = null->c[2] = null;
io >> n >> q;
for (int i = 1; i <= n; i++) io >> a[i];
for (int i = 1, u, v, w; i < n; i++) {
io >> u >> v >> w;
edge[u].emplace_back(v, w);
edge[v].emplace_back(u, w);
}
tot = n;
binarize(1, 0);
build(1, 1);
for (int i = 0; i <= n; i++) root[i] = null;
for (int i = 1; i <= n; i++) insert(root[i], root[i - 1], a[i], 1);
long long lastans = 0;
for (int cmd, l, r, u; q--;) {
io >> cmd;
switch (cmd) {
case 1: {
io >> l >> r >> u;
l ^= lastans;
r ^= lastans;
u ^= lastans;
lastans = query(root[r], u, 1) - query(root[l - 1], u, 1);
io << lastans << '\n';
lastans &= ((1 << 30) - 1);
break;
}
case 2: {
io >> u;
u ^= lastans;
erase(root[u], root[u], a[u], 1);
std::swap(a[u], a[u + 1]);
insert(root[u], root[u], a[u], 1);
break;
}
}
}
return 0;
}
分享到