【Luogu P4074】[WC2013]糖果公园(树上带修改莫队)
题目描述
Candyland 有一座糖果公园,公园里不仅有美丽的风景、好玩的游乐项目,还有许多免费糖果的发放点,这引来了许多贪吃的小朋友来糖果公园游玩。
糖果公园的结构十分奇特,它由 \(n\) 个游览点构成,每个游览点都有一个糖果发放处,我们可以依次将游览点编号为 \(1\) 至 \(n\)。有 \(n-1\) 条双向道路连接着这些游览点,并且整个糖果公园都是连通的,即从任何一个游览点出发都可以通过这些道路到达公园里的所有其它游览点。
糖果公园所发放的糖果种类非常丰富,总共有 \(m\) 种,它们的编号依次为 \(1\) 至 \(m\)。每一个糖果发放处都只发放某种特定的糖果,我们用 \(C_i\) 来表示 \(i\) 号游览点的糖果。
来到公园里游玩的游客都不喜欢走回头路,他们总是从某个特定的游览点出发前往另一个特定的游览点,并游览途中的景点,这条路线一定是唯一的。他们经过每个游览点,都可以品尝到一颗对应种类的糖果。
大家对不同类型糖果的喜爱程度都不尽相同。 根据游客们的反馈打分,我们得到了糖果的美味指数, 第 \(i\) 种糖果的美味指数为 \(V_i\) 。另外,如果一位游客反复地品尝同一种类的糖果,他肯定会觉得有一些腻。根据量化统计,我们得到了游客第 \(i\) 次品尝某类糖果的新奇指数 \(W_i\) 。如果一位游客第 \(i\) 次品尝第 \(j\) 种糖果,那么他的愉悦指数 \(H\) 将会增加对应的美味指数与新奇指数的乘积,即 \(V_j×W_i\) 。这位游客游览公园的愉悦指数最终将是这些乘积的和。
当然,公园中每个糖果发放点所发放的糖果种类不一定是一成不变的。有时,一些糖果点所发放的糖果种类可能会更改(也只会是 \(m\) 种中的一种),这样的目的是能够让游客们总是感受到惊喜。
糖果公园的工作人员小 A 接到了一个任务,那就是根据公园最近的数据统计出每位游客游玩公园的愉悦指数。但数学不好的小 A 一看到密密麻麻的数字就觉得头晕,作为小 A 最好的朋友,你决定帮他一把。
输入输出格式
输入格式:
从文件 park.in 中读入数据。
第一行包含三个正整数 \(n\), \(m\), \(q\), 分别表示游览点个数、 糖果种类数和操作次数。
第二行包含 \(m\) 个正整数 \(V_1,V_2,...,V_m\) 。
第三行包含 \(n\) 个正整数 \(W_1,W_2,...,W_n\) 。
第四行到第 \(n+2\) 行,每行包含两个正整数 \(A_i,B_i\) ,表示这两个游览点之间有路径可以直接到达。
第 \(n+3\) 行包含 \(n\) 个正整数 \(C_1,C_2,C_n\) 。
接下来 \(q\) 行, 每行包含三个整数 \(Type\), \(x\), \(y\),表示一次操作:
若 \(Type\) 为 \(0\),则 \(1 ≤ x ≤ n\), \(1 ≤ y ≤ m\),表示将编号为 \(x\) 的游览点发放的糖果类型改为 \(y\);
若 \(Type\) 为 \(1\),则 \(1 ≤ x, y ≤ n\),表示对出发点为 \(x\),终止点为 \(y\) 的路线询问愉悦指数。
输出格式:
输出到文件 park.out 中。
按照输入的先后顺序,对于每个 \(Type\) 为 \(1\) 的操作输出一行,用一个正整数表示答案。
输入输出样例
输入样例#1:
4 3 5
1 9 2
7 6 5 1
2 3
3 1
3 4
1 2 3 2
1 1 2
1 4 2
0 2 1
1 1 2
1 4 2
输出样例#1:
84
131
27
84
整体思路
树上莫队+带修莫队=树上带修莫队,如果不懂可以看这里
莫队添加数值时,添加的数值(添加的结点发的糖,记为\(c\))对答案的贡献为\(V_c × W_{cnt_c}\),在端点、修改点、lca暴力添加即可。删除亦然。
code:
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
using namespace std;
#define maxn 200200
#define ll long long
int cnt[maxn], aa[maxn], belong[maxn], inp[maxn], n, m, Q, ncnt, size, bnum, w[maxn], v[maxn], ccnt, qcnt;
int val[maxn], fa[maxn][30], depth[maxn], head[maxn], ecnt;
int fir[maxn], la[maxn], vis[maxn];
int l = 1, r = 0, t = 0;
ll now, ans[maxn];
struct edge {
int to, next;
} e[maxn];
void adde(int u, int v) {
e[++ecnt] = (edge){v, head[u]};
head[u] = ecnt;
e[++ecnt] = (edge){u, head[v]};
head[v] = ecnt;
}
void dfs(int x) {
aa[++ncnt] = x;
fir[x] = ncnt;
for(int k = head[x]; k; k = e[k].next) {
int to = e[k].to;
if(depth[to]) continue;
depth[to] = depth[x] + 1;
fa[to][0] = x;
for(int i = 1; (1 << i) <= depth[to]; ++i) fa[to][i] = fa[fa[to][i - 1]][i - 1];
dfs(to);
}
aa[++ncnt] = x;
la[x] = ncnt;
}
int getlca(int u, int v) {
if(depth[u] < depth[v]) swap(u, v);
for(int i = 20; i + 1; --i) if(depth[fa[u][i]] >= depth[v]) u = fa[u][i];
if(u == v) return u;
for(int i = 20; i + 1; --i) if(fa[u][i] != fa[v][i]) u = fa[u][i], v = fa[v][i];
return fa[u][0];
}
struct query {
int l, r, id, lca, t;
} q[maxn];
int cmp(query a, query b) {
return (belong[a.l] ^ belong[b.l]) ? belong[a.l] < belong[b.l] : ((belong[a.r] ^ belong[b.r]) ? belong[a.r] < belong[b.r] : a.t < b.t );
}
inline void add(int pos) {
now += 1ll * v[val[pos]] * w[++cnt[val[pos]]];
}
inline void del(int pos) {
now -= 1ll * v[val[pos]] * w[cnt[val[pos]]--];
}
inline void work(int pos) {
vis[pos] ? del(pos) : add(pos);
vis[pos] ^= 1;
}
struct change {
int pos, val;
} ch[maxn];
void modify(int x) {
if(vis[ch[x].pos]) {
work(ch[x].pos);
swap(val[ch[x].pos], ch[x].val);
work(ch[x].pos);
}
else swap(val[ch[x].pos], ch[x].val);
}
#define isdigit(x) ((x) >= '0' && (x) <= '9')
inline int read() {
int res = 0;
char c = getchar();
while(!isdigit(c)) c = getchar();
while(isdigit(c)) res = (res << 1) + (res << 3) + (c ^ 48), c = getchar();
return res;
}
int main() {
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();
for(int i = 1; i < n; ++i) {
int u = read(), v = read();
adde(u, v);
}
for(int i = 1; i <= n; ++i) val[i] = read();
depth[1] = 1;
dfs(1);
size = pow(ncnt, 2.0 / 3.0);
bnum = ceil((double)ncnt / size);
for(int i = 1; i <= bnum; ++i)
for(int j = size * (i - 1) + 1; j <= i * size; ++j) belong[j] = i;
for(int i = 1; i <= Q; ++i) {
int opt = read(), a = read(), b = read();
if(opt) {
int lca = getlca(a, b);
q[++qcnt].t = ccnt;
q[qcnt].id = qcnt;
if(fir[a] > fir[b]) swap(a, b);
if(a == lca) q[qcnt].l = fir[a], q[qcnt].r = fir[b];
else q[qcnt].l = la[a], q[qcnt].r = fir[b], q[qcnt].lca = lca;
}
else {
ch[++ccnt].pos = a;
ch[ccnt].val = b;
}
}
sort(q + 1, q + qcnt + 1, cmp);
for(int i = 1; i <= qcnt; ++i) {
int ql = q[i].l, qr = q[i].r, qt = q[i].t, qlca = q[i].lca;
while(l < ql) work(aa[l++]);
while(l > ql) work(aa[--l]);
while(r < qr) work(aa[++r]);
while(r > qr) work(aa[r--]);
while(t < qt) modify(++t);
while(t > qt) modify(t--);
if(qlca) work(qlca);
ans[q[i].id] = now;
if(qlca) work(qlca);
}
for(int i = 1; i <= qcnt; ++i) printf("%lld\n", ans[i]);
return 0;
}