【题目链接】
https://www.lydsy.com/JudgeOnline/problem.php?id=3052
【题解】
首先是树上莫队的一些套路:
1.如果是查询子树信息,那么可以选择按dfs序进行分块,这样查询的位置一定是一段区间。
2.如果是查询一条链的信息,那么欧拉序是首选,记进入时间为
, 离开时间为
,此时要分两种情况讨论(
):1).
不等于
或
,那么
到
的区间中出现次数为奇数的点以及
就是答案。2).
,那么
到
的区间中出现次数为奇数的点以及
就是答案。
通过这样的转换,链上问题也能变成区间问题。
接下来是修改,记每块的大小为
,以左端点所在块为第一关键字,右端点所在块为第二关键字,时间为第三关键字排序,然后转移时暴力修改就行了。可以证明这样做的时间复杂度是
【代码】
/* - - - - - - - - - - - - - - -
User : VanishD
problem : [bzoj3052]
Points : Mo's algorithm on tree with modification
- - - - - - - - - - - - - - - */
# include <bits/stdc++.h>
# define ll long long
# define inf 0x3f3f3f3f
# define N 200010
# define K 20
using namespace std;
int read(){
int tmp = 0, fh = 1; char ch = getchar();
while (ch < '0' || ch > '9'){ if (ch == '-') fh = -1; ch = getchar(); }
while (ch >= '0' && ch <= '9'){ tmp = tmp * 10 + ch - '0'; ch = getchar(); }
return tmp * fh;
}
struct Node{
int l, r, lca, ti, id;
}qy[N];
struct Edge{
int data, next;
}e[N * 2];
int out[N], in[N], p[N], n, m, q, v[N], head[N], place, chid[N], chto[N], chlas[N], now[N], c[N], dep[N], dad[N][K + 1], hav[N], ti, size, cnt[N];
ll ans[N], w[N], sum;
void build(int u, int v){
e[++place].data = v; e[place].next = head[u]; head[u] = place;
}
void dfs(int x, int fa){
in[x] = ++ti, p[ti] = x; dad[x][0] = fa; dep[x] = dep[fa] + 1;
for (int ed = head[x]; ed != 0; ed = e[ed].next)
if (e[ed].data != fa) dfs(e[ed].data, x);
out[x] = ++ti, p[ti] = x;
}
void pre(){
for (int i = 1, j = 1; j * 2 <= n; i++, j *= 2)
for (int k = 1; k <= n; k++)
dad[k][i] = dad[dad[k][i - 1]][i - 1];
}
int lca(int u, int v){
if (dep[u] > dep[v]) swap(u, v);
for (int i = K; i >= 0; i--)
if (dep[dad[v][i]] >= dep[u]) v = dad[v][i];
if (u == v) return u;
for (int i = K; i >= 0; i--)
if (dad[v][i] != dad[u][i])
v = dad[v][i], u = dad[u][i];
return dad[v][0];
}
bool cmp(Node x, Node y){
return (x.l / size < y.l / size) || (x.l / size == y.l / size && x.r / size < y.r / size) ||
(x.l / size == y.l / size && x.r / size == y.r / size && x.ti < y.ti);
}
inline void modifysum(int x, int vote){
sum = sum + w[x] * vote;
}
inline void inc(int x){
modifysum(cnt[x], -v[x]);
modifysum(++cnt[x], v[x]);
}
inline void dec(int x){
modifysum(cnt[x], -v[x]);
modifysum(--cnt[x], v[x]);
}
void modify(int id){
if (hav[id] == true)
dec(now[id]);
else inc(now[id]);
hav[id] = hav[id] ^ 1;
}
int main(){
// freopen("bzoj3052.in", "r", stdin);
// freopen("bzoj3052.out", "w", stdout);
n = read(), m = read(), q = read();
for (int i = 1; i <= m; i++) v[i] = read();
for (int i = 1; i <= n; i++) w[i] = read() + w[i - 1];
for (int i = 1; i < n; i++){
int u = read(), v = read();
build(u, v); build(v, u);
}
for (int i = 1; i <= n; i++) c[i] = now[i] = read();
dfs(1, 0); pre();
int qnum = 0, num = 0;
for (int i = 1; i <= q; i++){
int op = read();
if (op == 0){
chid[++qnum] = read();
chto[qnum] = read();
chlas[qnum] = now[chid[qnum]];
now[chid[qnum]] = chto[qnum];
}
else {
int u = read(), v = read();
qy[++num].ti= qnum; qy[num].id = num;
if (in[u] > in[v]) swap(u, v);
int tmp = lca(u, v);
qy[num].lca = tmp;
qy[num].r = in[v];
if (tmp == u)
qy[num].l = in[u] + 1;
else qy[num].l = out[u];
}
}
size = (int)pow(num, 0.67);
sort(qy + 1, qy + num + 1, cmp);
int pl = 1, pr = 0, pch = 0; sum = 0;
for (int i = 1; i <= n; i++) now[i] = c[i];
for (int i = 1; i <= num; i++){
for (int j = pch + 1; j <= qy[i].ti; j++){
bool tag = false;
if (hav[chid[j]] != 0) tag = true;
if (tag) modify(chid[j]);
now[chid[j]] = chto[j];
if (tag) modify(chid[j]);
}
for (int j = pch; j > qy[i].ti; j--){
bool tag = false;
if (hav[chid[j]] != 0) tag = true;
if (tag) modify(chid[j]);
now[chid[j]] = chlas[j];
if (tag) modify(chid[j]);
}
pch = qy[i].ti;
for (int j = pl - 1; j >= qy[i].l; j--) modify(p[j]);
for (int j = pr + 1; j <= qy[i].r; j++) modify(p[j]);
for (int j = pl; j < qy[i].l; j++) modify(p[j]);
for (int j = pr; j > qy[i].r; j--) modify(p[j]);
pl = qy[i].l, pr = qy[i].r;
modify(qy[i].lca);
ans[qy[i].id] = sum;
modify(qy[i].lca);
}
for (int i = 1; i <= num; i++)
printf("%lld\n", ans[i]);
return 0;
}