「洛谷P3401」洛谷树 题解

题目

分析

\(s_u\)\(u\)到根的路径上的边权异或和, \(s_{u,v}\)\(u\)\(v\)的简单路径上的边权异或和

由树上差分的知识可得\(s_{u,v}=s_u\otimes s_v\otimes s_{lca}\otimes s_{lca}=s_u\otimes s_v\)

原问题可化为求\(\sum_{i,j\in\left\langle u,v\right\rangle}s_i\otimes s_j\)

做法

考虑维护每一位,若路径\(\left\langle u,v\right\rangle\)经过\(siz\)个结点,其中\(s\)的第\(i\)位为\(1\)的有\(cnt\)个,则为\(0\)的有\((siz-cnt)\)个第\(i\)位对答案的贡献为\(cnt\cdot(siz-cnt)\cdot 2^i\)(一个\(0\)一个\(1\)才对答案有贡献)

路径上维护\(1\)的个数,容易想到树链剖分加线段树

修改操作中,将边\((u,v)(dep_u<dep_v)\)的权值修改为\(w\),原权值为\(w_b\),若\(w\)的第\(i\)位与\(w_b\)的第\(i\)位不同,则将\(v\)所在子树的\(s\)的第\(i\)位翻转即可,具体在线段树上,对于原本有\(cnt\)\(1\)的区间\(\left[l,r\right]\),将\(1\)的个数改为\((r-l+1-cnt)\)

代码

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
const int N = 30005;
const int M = 60005;
#define lc(x) (x << 1)
#define rc(x) (x << 1 | 1)

void add(int, int, int);
void dfs1(int);
void dfs2(int);
void build(int, int, int);
void update(int, int, int);
ll query(int, int);

int hed[N], nxt[M], to[M], val[M], id;
int dep[N], fa[N], siz[N], son[N], top[N], ttl[N], ltt[N], tot;
int vf[N];
int xs[N];
int sum[N << 2][10];
bool tag[N << 2][10];
int n, q;

int main () {
read(n), read(q);
for (int i = 1; i < n; ++i) {
int u, v, w;
read(u), read(v), read(w);
add(u, v, w), add(v, u, w);
}
dfs1(1);
top[1] = 1;
dfs2(1);
build(1, 1, n);
for (int i = 1; i <= q; ++i) {
int type;
read(type);
int u, v;
read(u), read(v);
switch (type) {
case 1:
write(query(u, v)), EL;
break;
case 2:
int w;
read(w);
update(u, v, w);
break;
}
}
return 0;
}

inline void add(int u, int v, int w) {
nxt[++id] = hed[u];
hed[u] = id;
to[id] = v;
val[id] = w;
}
void dfs1(int u) {
siz[u] = 1;
for (int i = hed[u]; i; i = nxt[i]) {
int v = to[i];
if (v != fa[u]) {
dep[v] = dep[u] + 1;
fa[v] = u;
xs[v] = xs[u] ^ val[i];
vf[v] = val[i];
dfs1(v);
siz[u] += siz[v];
if (siz[v] > siz[son[u]]) {
son[u] = v;
}
}
}
}
void dfs2(int u) {
ltt[ttl[u] = ++tot] = u;
if (son[u]) {
top[son[u]] = top[u];
dfs2(son[u]);
}
for (int i = hed[u]; i; i = nxt[i]) {
int v = to[i];
if (v != fa[u] && v != son[u]) {
top[v] = v;
dfs2(v);
}
}
}
inline void pushdown(int x, int xl, int xr, int bit) {
if (tag[x][bit]) {
int xm = (xl + xr) >> 1;
sum[lc(x)][bit] = (xm - xl + 1) - sum[lc(x)][bit];
sum[rc(x)][bit] = (xr - xm) - sum[rc(x)][bit];
tag[lc(x)][bit] ^= 1;
tag[rc(x)][bit] ^= 1;
tag[x][bit] = false;
}
}
inline void pushup(int x, int bit) {
sum[x][bit] = sum[lc(x)][bit] + sum[rc(x)][bit];
}
void update(int x, int xl, int xr, int bit, int ul, int ur) {
if (xl >= ul && xr <= ur) {
sum[x][bit] = (xr - xl + 1) - sum[x][bit];
tag[x][bit] ^= 1;
return;
}
pushdown(x, xl, xr, bit);
int xm = (xl + xr) >> 1;
if (xm >= ul) {
update(lc(x), xl, xm, bit, ul, ur);
}
if (xm < ur) {
update(rc(x), xm + 1, xr, bit, ul, ur);
}
pushup(x, bit);
}
int query(int x, int xl, int xr, int bit, int ql, int qr) {
if (xl >= ql && xr <= qr) {
return sum[x][bit];
}
pushdown(x, xl, xr, bit);
int xm = (xl + xr) >> 1;
int ans = 0;
if (xm >= ql) {
ans += query(lc(x), xl, xm, bit, ql, qr);
}
if (xm < qr) {
ans += query(rc(x), xm + 1, xr, bit, ql, qr);
}
return ans;
}
void build(int x, int xl, int xr) {
if (xl == xr) {
for (int i = 0; i < 10; ++i) {
sum[x][i] = (xs[ltt[xl]] >> i) & 1;
}
return;
}
int xm = (xl + xr) >> 1;
build(lc(x), xl, xm);
build(rc(x), xm + 1, xr);
for (int i = 0; i < 10; ++i) {
pushup(x, i);
}
}
void update(int u, int v, int w) {
int x = dep[u] < dep[v] ? v : u;
for (int i = 0; i < 10; ++i) {
if (((vf[x] ^ w) >> i) & 1) {
update(1, 1, n, i, ttl[x], ttl[x] + siz[x] - 1);
}
}
vf[x] = w;
}
ll query(int u, int v) {
int cnt[10];
int siz = 0;
for (int i = 0; i < 10; ++i) {
cnt[i] = 0;
}
while (top[u] ^ top[v]) {
if (dep[top[u]] < dep[top[v]]) {
u ^= v ^= u ^= v;
}
siz += ttl[u] - ttl[top[u]] + 1;
for (int i = 0; i < 10; ++i) {
cnt[i] += query(1, 1, n, i, ttl[top[u]], ttl[u]);
}
u = fa[top[u]];
}
if (dep[u] < dep[v]) {
u ^= v ^= u ^= v;
}
siz += ttl[u] - ttl[v] + 1;
for (int i = 0; i < 10; ++i) {
cnt[i] += query(1, 1, n, i, ttl[v], ttl[u]);
}
ll ans = 0;
for (int i = 0; i < 10; ++i) {
ans += cnt[i] * (siz - cnt[i]) * 1ll * (1 << i);
}
return ans;
}

「洛谷P3401」洛谷树 题解
https://blog.seniorious.cc/2019/luogu-3401/
作者
Seniorious
发布于
2019年12月22日
许可协议