zqs`s blog

有朋自远方来,虽远必诛

bzoj 3683 Falsita

这题的题面非常的有诗意,大意就是给你一颗 $n$ 个点的树,点有点权,$m$ 和操作,操作有:

  • 把 $u$ 的点权 $+\Delta$。
  • 把 $u$ 所在子树的所有点权全部加上 $\Delta$。
  • 在所有满足 $(x,y)$​​ 的 $lca$​​ 是 $u$​ 的点对中随意选出一个,求 $w_x+w_y$ 的期望值($w$ 为点权)。

先考虑这个奇怪的3操作。

设 $sze_u$ 表示 $u$ 所在子树的大小,$sum_u$ 表示 $u$ 所在子树所有点权之和。

那么3操作的答案即为($v$ 是 $u$ 的儿子,$size_u$ 为 $u$ 子节点个数)

​​

令 $f_u=(size_u^2-\sum size_v^2-1)/2,g_u=sum_u\times size_u-\sum sum_v\times size_v-w_u$

则 $f_u$ 表示有多少个点对 $(x,y)$ 满足 $x,y$ 的 $lca$ 是 $u$,$g_u$ 表示所有满足询问满足 $(x,y)$ 的 $lca$ 是 $u$ 的 $(x,y)$ 点权总和。

先 dfs 一遍求出 $f$ 和 $g$​ ,接下来考虑两个修改:

  • 单点修改

    先调整 $f_u$ 的值。

    然后考虑它对祖先的影响。假设 $x$ 为 $u$ 的祖先,$y$ 为 $x$ 在 $u$ 方向的儿子。

    则 $sum_x$ 增加了 $\Delta$,$sum_y$ 也增加了 $\Delta$,$f_x$ 增加了 $\Delta\times (size_x-size_y)$。

    暴力往上枚举每个祖先不可取,但这个 $y$ 又是根据 $x$ 的不同不断变化的,怎么办?遇树不决,树链剖分

    如果 $u$ 沿着树链剖分的重链往上跳,那么由于只会经过 $\log n$ 条轻边,所以最多只有 $\log n$ 个 $u$ 的祖先 $x$ 在 $u$ 方向的儿子不是它的重儿子。

    所以遇到轻边暴力调整 $f$,遇到重链用树状数组/线段树维护一下,设 $son_x$ 为 $x$ 的重儿子,则 $size_x-size_{son_x}$ 是一个定值,只需要询问的时候乘上去就可以了。所以开一颗树状数组/线段树,把一条重链在树状数组上对应的区间全部加上 $\Delta$,询问的时候再把 $f_u$ 加上树状数组中 $u$ 点的值乘上 $size_u-size_{son_u}$。

  • 子树修改

    这个操作从整体考虑,子树内的每个点的答案(注意是最后期望值的答案不是 $f$)全都加上了 $2\times \Delta$​(每个点对都加上了这么多点权)。这里又要开一颗树状数组/线段树(如果是线段树其实不用新开一颗),维护一下这个子树答案的整体加。

    然后对祖先的影响就是把单点修改i的 $\Delta$ 变成了 $size_u\times \Delta$,可以直接粘单点修改的代码。

查询的时候需要查询两颗线段树。

代码很短,但有一些细节。

代码
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
#include <cstdio>
#include <vector>
#define int long long

std::vector<int> G[300005];
int son[300005], top[300005], sze[300005], fa[300005], f[300005], n, m;
int In[300005], Out[300005], g[300005], w[300005], sum[300005], cnt;
struct BIT {
int c[300005];
void update(int l, int r, int d) {
for (int i = l; i <= n; i += (i & ~i + 1)) c[i] += d;
for (int i = r + 1; i <= n; i += (i & ~i + 1)) c[i] -= d;
}
int query(int x) {
int sum = 0;
for (int i = x; i; i -= (i & ~i + 1)) sum += c[i];
return sum;
}
} b, ans;
void dfs1(int u) {
In[u] = ++ cnt, sze[u] = 1, sum[u] = w[u], f[u] = -1, g[u] = -w[u];
for (int v : G[u]) {
dfs1(v), sze[u] += sze[v], sum[u] += sum[v], f[u] -= sze[v] * sze[v];
g[u] -= sum[v] * sze[v];
if (sze[v] > sze[son[u]]) son[u] = v;
}
g[u] += sum[u] * sze[u], f[u] += sze[u] * sze[u], f[u] >>= 1, Out[u] = cnt;
}
void dfs2(int u) {
if (son[u]) top[son[u]] = top[u], dfs2(son[u]);
for (int v : G[u])
if (v != son[u]) top[v] = v, dfs2(v);
}
void change1(int u, int delta) {
int lst = u;
g[u] += (sze[u] - 1) * delta, u = fa[u];
while (u) {
if (son[u] != lst) {
g[u] += delta * (sze[u] - sze[lst]);
lst = u, u = fa[u]; continue;
}
b.update(In[top[u]], In[u], delta);
lst = top[u], u = fa[top[u]];
}
}
void change2(int u, int delta) {
int lst = u;
ans.update(In[u], Out[u], delta << 1);
delta *= sze[u], u = fa[u];
while (u) {
if (son[u] != lst) {
g[u] += delta * (sze[u] - sze[lst]);
lst = u, u = fa[u]; continue;
}
b.update(In[top[u]], In[u], delta);
lst = top[u], u = fa[top[u]];
}
}
double query(int u) {
return ans.query(In[u]) + 1.0 * (g[u] + (sze[u] - sze[son[u]]) * b.query(In[u])) / f[u];
}

signed main() {
scanf("%lld%lld", &n, &m);
for (int i = 2; i <= n; ++ i)
scanf("%lld", fa + i), G[fa[i]].push_back(i);
for (int i = 1; i <= n; ++ i) scanf("%lld", w + i);
dfs1(1), top[1] = 1, dfs2(1);
while (m --) {
char opt;
int u, delta;
scanf(" %c%lld", &opt, &u);
if (opt == 'S') scanf("%lld", &delta), change1(u, delta);
else if (opt == 'M') scanf("%lld", &delta), change2(u, delta);
else if (opt == 'Q') printf("%.6lf\n", query(u));
}
return 0;
}
-------------本文结束感谢您的阅读-------------