「题解」洛谷 P4074 [WC2013]糖果公园
题目
简化题意
给你一棵树树,点有点权,带修改,每一次经过一种点权会有不同的贡献(随着经过次数再变),问你从一个点到一个点的贡献和
思路
树上带修莫队。
用 \(cnt[i]\) 表示 \(i\) 这个点的权值的出现次数。
用 \(a[i]\) 表示 \(i\) 号点的权值。
用 \(v[i]\) 表示一权值 \(i\) 的贡献定值。
用 \(v[i]\) 表示一权值出现 \(i\) 次时的贡献常数。
加入一个点 \(u\):ans += v[++cnt[a[u]]] * w[a[u]]
删掉一个点 \(u\):ans -= v[cnt[a[u]]--] * w[a[u]]
先考虑没有修改,因为加一个点和删一个点都可以 \(O(1)\) 维护,所以莫队没问题。
在树上,树上莫队。
如果没有修改就是一个普通的树上莫队。有修改就是树上带修莫队了。
Code
#include <cmath>
#include <cstdio>
#include <cstring>
#include <string>
#include <iostream>
#include <algorithm>
#define MAXN 100001
inline void read(int &T) {
int x = 0;
bool f = 0;
char c = getchar();
while (c < '0' || c > '9') {
if (c == '-') f = !f;
c = getchar();
}
while (c >= '0' && c <= '9') {
x = x * 10 + c - '0';
c = getchar();
}
T = f ? -x : x;
}
void write(long long x) {
if (x < 0) putchar('-'), write(-x);
else {
if (x / 10) write(x / 10);
putchar(x % 10 + '0');
}
}
typedef long long ll;
ll ans[MAXN];
int n, m, s, sqrn, pthn, head[MAXN], a[MAXN], v[MAXN], w[MAXN], num[MAXN << 1], cnt[MAXN], vis[MAXN];
int c_, cc, cq, fir[MAXN], la[MAXN], pre[MAXN << 1], dep[MAXN], lg[MAXN], fa[MAXN][21];
struct change {
int w, pos;
}c[MAXN];
struct query {
int x, y, t, lca, id;
friend bool operator < (query q1, query q2) {
if (num[q1.x] != num[q2.x]) return num[q1.x] < num[q2.x];
if (num[q1.y] != num[q2.y]) return num[q1.y] < num[q2.y];
return q1.t < q2.t;
}
}q[MAXN];
struct Edge {
int next ,to;
}pth[MAXN << 1];
void add(int from, int to) {
pth[++pthn].to = to, pth[pthn].next = head[from];
head[from] = pthn;
}
void dfs(int u, int father) {
fa[u][0] = father, dep[u] = dep[father] + 1;
fir[u] = ++c_, pre[c_] = u;
for (int i = head[u]; i; i = pth[i].next) {
int x = pth[i].to;
if (x != father) dfs(x, u);
}
la[u] = ++c_, pre[c_] = u;
}
int lca(int x, int y) {
if (dep[x] < dep[y]) std::swap(x, y);
while (dep[x] > dep[y]) {
x = fa[x][lg[dep[x] - dep[y]] - 1];
}
if (x == y) return x;
for (int k = lg[dep[x]] - 1; k >= 0; --k) {
if (fa[x][k] != fa[y][k]) {
x = fa[x][k];
y = fa[y][k];
}
}
return fa[x][0];
}
void work(int now, ll &ans) {
if (vis[now]) {
ans -= 1ll * w[cnt[a[now]]] * v[a[now]];
--cnt[a[now]];
}
else {
++cnt[a[now]];
ans += 1ll * w[cnt[a[now]]] * v[a[now]];
}
vis[now] ^= 1;
}
bool okay(int nq, int node) {
bool flag1 = (fir[node] >= q[nq].x && fir[node] <= q[nq].y && (la[node] < q[nq].x || la[node] > q[nq].y));
bool flag2 = (la[node] >= q[nq].x && la[node] <= q[nq].y && (fir[node] < q[nq].x || fir[node] > q[nq].y));
return (flag1 || flag2);
}
int main() {
read(n), read(m), read(s), sqrn = pow(2 * n, 2.0 / 3.0);
for (int i = 1; i <= 2 * n; ++i) num[i] = (i - 1) / sqrn + 1;
for (int i = 1; i <= m; ++i) read(v[i]);
for (int i = 1; i <= n; ++i) read(w[i]);
for (int i = 1, u, v; i < n; ++i) {
read(u), read(v);
add(u, v), add(v, u);
}
dfs(1, 0);
for (int i = 1; i <= n; ++i) {
lg[i] = lg[i - 1] + ((1 << lg[i - 1]) == i);
}
for (int j = 1; (1 << j) <= n; ++j) {
for (int i = 1; i <= n; ++i) {
fa[i][j] = fa[fa[i][j - 1]][j - 1];
}
}
for (int i = 1; i <= n; ++i) read(a[i]);
for (int i = 1, opt, x, y, f; i <= s; ++i) {
read(opt), read(x), read(y);
if (opt == 1) {
f = lca(x, y), q[++cq].id = cq, q[cq].t = cc;
if (fir[x] > fir[y]) std::swap(x, y);
if (x == f) {
q[cq].x = fir[x];
q[cq].y = fir[y];
}
else {
q[cq].x = la[x];
q[cq].y = fir[y];
q[cq].lca = f;
}
}
else c[++cc].w = y, c[cc].pos = x;
}
std::sort(q + 1, q + cq + 1);
int l = 1, r = 0, t = 0;
ll now = 0;
for (int i = 1; i <= cq; ++i) {
while (l > q[i].x) work(pre[--l], now);
while (r < q[i].y) work(pre[++r], now);
while (l < q[i].x) work(pre[l++], now);
while (r > q[i].y) work(pre[r--], now);
while (t < q[i].t) {
++t;
bool flag = okay(i, c[t].pos);
if (flag) work(c[t].pos, now);
std::swap(a[c[t].pos], c[t].w);
if (flag) work(c[t].pos, now);
}
while (t > q[i].t) {
bool flag = okay(i, c[t].pos);
if (flag) work(c[t].pos, now);
std::swap(a[c[t].pos], c[t].w);
if (flag) work(c[t].pos, now);
--t;
}
if (q[i].lca) work(q[i].lca, now);
ans[q[i].id] = now;
if (q[i].lca) work(q[i].lca, now);
}
for (int i = 1; i <= cq; ++i) {
write(ans[i]);
puts("");
}
return 0;
}