题解 CF809E 【Surprise me!】
怎么题解区一大片虚树,给个不用虚树的做法。
按照套路拆贡献干掉 dis,对于每一个点
设
则我们希望对于所有
考虑启发式合并。问题可以转化为如下形式:对于两个不交点集
这样问题就转化为求:
这个形式对于启发式合并就友好很多了。不妨设
设
这看起来就是一个很标准的数论推式子题了。尝试计算这个东西。
设:
显然
那么如何快速维护
- 先对整个树轻重链剖分。
- 在树上 DFS,先 DFS 轻儿子,然后扫一遍轻儿子的所有孩子,撤销其对后缀和的影响(即,清空)。
- 最后 DFS 重儿子,继承其 Dirichlet 后缀和数组。
- 按照上面的式子进行合并。
- 暴力用轻儿子的子树里面的点更新 Dirichlet 后缀和数组,即直接往所有约数上面加。这样做,复杂度沿用上面的分析,是
O(n\log^2n) 。
这样即可做到实时维护 Dirichlet 后缀和,那么就可以直接按照上面的式子进行合并。然后这题就做完了。总复杂度
放个核心代码。
// calc sum_{i=1...n}phi(ki)*c[i]
inline long long Sum(long long k, long long *cds) {
long long ans = 0;
for (int x : d[k]) ans = (ans + fv[x] * cds[x]) % mod;
return ans * phi[k] % mod;
}
inline void Prefix() {
for (int i = 1;i <= n;i++) {
for (int j = i;j <= n;j += i) d[j].push_back(i);
}
for (int i = 1;i <= n;i++) {
int tmp = i;
for (int j = 2;j * j <= tmp;j++) {
if (tmp % j == 0) {
p[i].push_back(j);
while (tmp % j == 0) tmp /= j;
}
}
if (tmp > 1) p[i].push_back(tmp);
}
phi[1] = 1; mu[1] = 1;
for (int i = 2;i <= n;i++) {
if (!vis[i]) {
phi[i] = i - 1;
pri[++pcnt] = i;
mu[i] = -1;
}
for (int j = 1;j <= pcnt && i * pri[j] <= n;j++) {
vis[i * pri[j]] = 1;
if (i % pri[j] == 0) {
phi[i * pri[j]] = phi[i] * pri[j];
mu[i * pri[j]] = 0;
break;
}
phi[i * pri[j]] = phi[i] * (pri[j] - 1);
mu[i * pri[j]] = -mu[i];
}
}
for (int i = 1;i <= n;i++) piv[i] = Power(phi[i], mod - 2);
for (int i = 1;i <= n;i++) {
for (int x : d[i]) {
fv[i] = (fv[i] + mu[i / x] * x * piv[x] % mod + mod) % mod;
}
}
for (int i = 1;i <= n;i++) {
for (int x : d[i]) cnt[x] = (cnt[x] + phi[i]) % mod;
}
for (int i = 1;i <= n;i++) dp[i] = (Sum(a[i], cnt) - a[i] * phi[a[i]] % mod + mod) % mod;
memset(cnt, 0, sizeof(cnt));
}
inline void Dfs1(int u, int fa) {
siz[u] = 1;
for (int i = hd[u];~i;i = e[i].nxt) {
if (e[i].to != fa) {
Dfs1(e[i].to, u);
siz[u] += siz[e[i].to];
if (siz[son[u]] < siz[e[i].to]) son[u] = e[i].to;
}
}
}
inline void Dfs2(int u, int fa) {
v[u].push_back(a[u]);
for (int i = hd[u];~i;i = e[i].nxt) {
if (e[i].to != fa && e[i].to != son[u]) {
Dfs2(e[i].to, u);
for (int x : v[e[i].to]) {
v[u].push_back(x);
for (int y : d[x]) cnt[y] = 0;
}
if (v[e[i].to].size() > v[u].size()) swap(v[u], v[e[i].to]);
}
}
if (son[u]) {
Dfs2(son[u], u);
swap(v[u], v[son[u]]);
for (int x : v[son[u]]) v[u].push_back(x);
v[son[u]].clear();
long long tmp = Sum(a[u], cnt);
dp[u] = (dp[u] + dp[son[u]] - 2 * tmp + 2 * mod) % mod;
}
for (int x : d[a[u]]) cnt[x] = (cnt[x] + phi[a[u]]) % mod;
for (int i = hd[u];~i;i = e[i].nxt) {
if (e[i].to != fa && e[i].to != son[u]) {
long long sum = 0;
for (int x : v[e[i].to]) sum = (sum + Sum(x, cnt)) % mod;
dp[u] = (dp[u] + dp[e[i].to] - 2 * sum + 2 * mod) % mod;
for (int x : v[e[i].to]) {
for (int y : d[x]) cnt[y] = (cnt[y] + phi[x]) % mod;
}
v[e[i].to].clear();
}
}
if (fa >= 0) ans = (ans + dp[u]) % mod;
}