P10121 『STA - R4』保险丝 题解
验题人题解。
为了方便描述,我们称题面中 $\prod\limits_{\substack{v\in\operatorname{subtree}(u)\\v\in\mathring U(x)}}F_{\deg v}$ 为 $u$ 对 $x$ 的贡献。
称度数 $\ge 3$ 的点是枢纽点,然后你要观察出一个阴间的性质:
每个点半邻域内的枢纽点个数之和是 $O(n\log n)$ 的。
(实际上,你可以通过暴力能过 Subtask 3 发现,
每个点半邻域内的枢纽点个数之和是一个可以接受的量级)
证明的话,感知一下可以发现完全二叉树可以使得每个点半邻域内的枢纽点个数之和最大,
而每个点半邻域内的枢纽点个数之和一定不超过每个点子树大小之和,也就是 $O(n\log n)$。
对每个枢纽点找半邻域内有它的点,这样预处理出每个点的半邻域内的枢纽点集。
对点 $u$ 半邻域内的枢纽点 $v$,设在 $u$ 的半邻域内,$v$ 上方第一个枢纽点为 $w$,
(若在 $u$ 的半邻域内,$v$ 上方没有枢纽点,$w$ 为 $u$ 的父亲)
则对 $[v,w)$ 上所有点 $x$,$x$ 对 $u$ 的贡献都一样,即都等于 $v$ 对 $u$ 的贡献。
于是求 $f(u)$ 时,只需对每个其半邻域内的枢纽点 $v$,统计 $v$ 以及与 $v$ 贡献相同的点的贡献,
没有被统计到的点贡献显然都是 $1$,简单加上它们的贡献即可。
考虑如何求每个 $v$ 的贡献,按 DFS 序从大到小加入每个 $v$,
则每个 $v$ 的贡献即为加入其后其子树内每个已加入的点 $i$ 的 $F_{\text{deg}_i}$ 之积,线段树维护之。
注意求 $f(1)$ 时可能需要一些特判。
#include <cstdio>
#include <vector>
#include <algorithm>
#define M 994007158
#define int long long
using namespace std;
struct T
{
int c[1000050 << 2];
void B(int s, int t, int p)
{
c[p] = 1;
if (s == t)
return;
int m = s + t >> 1;
B(s, m, p << 1);
B(m + 1, t, p << 1 | 1);
}
void C(int l, int x, int s, int t, int p)
{
if (s == t)
return c[p] = x, void();
int m = s + t >> 1;
if (l <= m)
C(l, x, s, m, p << 1);
else
C(l, x, m + 1, t, p << 1 | 1);
c[p] = c[p << 1] * c[p << 1 | 1] % M;
}
int Q(int l, int r, int s, int t, int p)
{
if (l <= s && t <= r)
return c[p];
int m = s + t >> 1, q = 1;
if (l <= m)
q = q * Q(l, r, s, m, p << 1) % M;
if (r > m)
q = q * Q(l, r, m + 1, t, p << 1 | 1) % M;
return q;
}
} Y;
struct E
{
int v, t;
} e[2000050];
vector<int> v[1000050], w[1000050];
int n, c, l, _, Z, a[1000050], b[1000050], F[1000050], S[1000050], p[1000050], o[1000050],
d[1000050], f[1000050], s[1000050], k[1000050], h[1000050], O[1000050], G[1000050];
void U(int x)
{
for (; x <= n; x += x & -x)
++O[x];
}
int Q(int x, int y)
{
int q = 0;
for (--x; y > x; y &= y - 1)
q += O[y];
for (; x > y; x &= x - 1)
q -= O[x];
return q;
}
void A(int u, int v) { e[++c] = {v, h[u]}, h[u] = c; }
void D(int u)
{
if (o[u] > 2)
p[u] = S[l], S[++l] = u;
b[u] = ++_;
s[u] = 1;
if (h[u])
k[u] = 1e9;
for (int i = h[u], v; i; i = e[i].t)
D(v = e[i].v), k[u] = min(k[u], k[v] + 1), s[u] += s[v];
if (o[u] > 2)
--l;
}
bool B(int x, int y) { return d[x] - k[x] < d[y] - k[y]; }
signed main()
{
scanf("%lld", &n);
w[d[1] = F[1] = o[1] = 1].push_back(1);
for (int i = 1; i <= n; ++i)
a[i] = i;
for (int i = 2; i <= n; ++i)
{
scanf("%lld", f + i), A(f[i], i), ++o[i], ++o[f[i]];
w[d[i] = d[f[i]] + 1].push_back(i), F[i] = (F[i - 1] + F[i - 2]) % M;
}
D(1);
for (int i = 2; i <= n; ++i)
k[i] = min(k[i], k[f[i]]);
sort(a + 1, a + n + 1, B);
for (int i = 1, j = 0; i <= n; ++i)
{
while (j < n && d[a[j + 1]] - k[a[j + 1]] <= i)
++j, U(b[a[j]]);
for (auto k : w[i])
G[k] = Q(b[k], b[k] + s[k] - 1);
}
for (int i = n; i >= 1; --i)
if (o[i] > 2)
{
for (int j = i; j && d[i] - d[j] <= k[i]; j = f[j])
v[j].push_back(i);
}
--o[1];
Y.B(1, n, 1);
for (int i = 1; i <= n; ++i)
{
int q = 0;
for (auto j : v[i])
{
Y.C(b[j], F[o[j]], 1, n, 1);
q = (q + Y.Q(b[j], b[j] + s[j] - 1, 1, n, 1) * (d[j] - max(d[p[j]], d[i] - 1)) % M) % M;
G[i] -= d[j] - max(d[p[j]], d[i] - 1);
}
Z ^= q + G[i];
for (auto j : v[i])
Y.C(b[j], 1, 1, n, 1);
}
printf("%lld", Z);
return 0;
}