0928模拟赛 树形熔断器
在计算以 为根的子树的答案时,只需要知道会经过 的方案的和,再加上儿子的答案。
对于子树内的点 ,假设它是 在 方向的儿子,要让它成为最小值,由于算的是经过 的方案,那么 上的所有点都会成为这种方案经过的点。因此 必须大于所有这些点。称 为关键点,定义一个关键点 的管辖范围为:以 为根的子树减去所有离他最近的(即到 的路径上没有其它关键点的)关键点为根的子树。它的 定义为管辖范围大小。
假设我们已经抓出了这些点 ,考虑如何计算方案数。
要让 成为最小值,只能选权值(即编号)大于它的关键点的的管辖范围,而且还必须选一个除了 子树内以外的 子树内的点。不难想到先计算不管“必须选一个除了 子树内以外的 子树内的点”的限制的方案,再减去只选 子树内的点的方案即可。令权值大于 的关键点的 之和为 ,所有方案的值总和显然为 。据此可以计算答案。
现在考虑如何抓出所有关键点。使用线段树合并,假设当前要和 合并,那么先删掉 内所有权值大于 的关键点。这不会影响其它点的管辖范围,因为一个关键点的后代关键点权值肯定比它要小。然后再减去只选 子树内的点的方案,再合并到 的线段树上。注意以 为最小值的情况需要单独计算。上面那个算答案的式子需要拆成两项维护,每个节点还要维护 和,所以总共要维护三个信息。因为删掉的关键点是一段后缀,所以只需要删掉后整体打个标记除以 的删掉的部分的 和次方即可。注意合并时 也会变,需要更新。
时间复杂度 。
比树剖套线段树套矩阵的垃圾官方题解不知道高到哪里去了
#include <cstdio>
#define gc (p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 100000, stdin), p1 == p2) ? EOF : *p1 ++)
const int mod = 998244353;
char buf[100000], *p1, *p2;
inline void add(int &x, const int y) {
if ((x += y) >= mod) x -= mod;
}
inline int plus(const int x, const int y) {
return x + y < mod ? x + y : x + y - mod;
}
inline int read() {
char ch;
int x = 0;
while ((ch = gc) < 48);
do x = x * 10 + ch - 48; while ((ch = gc) >= 48);
return x;
}
struct Edge {int to, nxt;} e[200005];
int head[200005], cnt[200005], root[200005], pow2[200005], inv2[200005], ans[200005], tot, n, suf1, suf2;
int ls[4000005], rs[4000005], sze[4000005], sum1[4000005], sum2[4000005], tag[4000005];
inline void AddEdge(int u, int v) {
e[++ tot].to = v, e[tot].nxt = head[u], head[u] = tot;
}
inline void pushup(int p) {
sze[p] = sze[ls[p]] + sze[rs[p]];
sum1[p] = plus(sum1[ls[p]], sum1[rs[p]]);
sum2[p] = plus(sum2[ls[p]], sum2[rs[p]]);
}
inline void pushdown(int p) {
if (tag[p] == 0) {
sze[ls[p]] = sum1[ls[p]] = sum2[ls[p]] = tag[ls[p]] = 0;
sze[rs[p]] = sum1[rs[p]] = sum2[rs[p]] = tag[rs[p]] = 0;
} else if (tag[p] != 1) {
sum1[ls[p]] = 1ll * sum1[ls[p]] * tag[p] % mod, tag[ls[p]] = 1ll * tag[ls[p]] * tag[p] % mod;
sum1[rs[p]] = 1ll * sum1[rs[p]] * tag[p] % mod, tag[rs[p]] = 1ll * tag[rs[p]] * tag[p] % mod;
}
tag[p] = 1;
}
void insert(int &p, int l, int r, int x, int s, int d, int v) {
if (!p) p = ++ tot, tag[p] = 1;
sze[p] += s, add(sum1[p], d), add(sum2[p], v);
if (l == r) return;
pushdown(p);
int mid = l + r >> 1;
x <= mid ? insert(ls[p], l, mid, x, s, d, v) : insert(rs[p], mid + 1, r, x, s, d, v);
}
void clear(int p, int l, int r, int x) {
if (x <= l) {sze[p] = sum1[p] = sum2[p] = tag[p] = 0; return;}
int mid = l + r >> 1;
pushdown(p);
if (x <= mid && ls[p]) clear(ls[p], l, mid, x);
if (rs[p]) clear(rs[p], mid + 1, r, x);
pushup(p);
}
void merge(int &u, int v) {
if (!u) {
sum1[v] = 1ll * sum1[v] * suf1 % mod, tag[v] = 1ll * tag[v] * suf1 % mod;
suf2 = 1ll * suf2 * pow2[sze[v]] % mod, u = v;
return;
}
if (!v) {
sum1[u] = 1ll * sum1[u] * suf2 % mod, tag[u] = 1ll * tag[u] * suf2 % mod;
suf1 = 1ll * suf1 * pow2[sze[u]] % mod;
return;
}
pushdown(u), pushdown(v), merge(rs[u], rs[v]), merge(ls[u], ls[v]);
pushup(u);
}
void dfs(int u) {
cnt[u] = 1;
int Size = 1;
for (int i = head[u]; i; i = e[i].nxt) {
int v = e[i].to;
dfs(v), cnt[u] += cnt[v];
int delta = sze[root[v]];
clear(root[v], 1, n, u), delta -= sze[root[v]];
sum1[root[v]] = 1ll * sum1[root[v]] * inv2[delta] % mod;
tag[root[v]] = 1ll * tag[root[v]] * inv2[delta] % mod;
ans[u] = (1ll * ans[u] - 1ll * sum1[root[v]] * pow2[cnt[v] - sze[root[v]]] + 1ll * sum2[root[v]]) % mod;
suf1 = suf2 = 1, merge(root[u], root[v]), ans[u] = (ans[u] + ans[v]) % mod;
ans[u] = (ans[u] - 1ll * (pow2[cnt[v] - sze[root[v]]] - 1) * u) % mod;
Size += cnt[v] - sze[root[v]];
}
ans[u] = (ans[u] + 1ll * (pow2[Size] - 1) * u) % mod;
sum1[root[u]] = 1ll * sum1[root[u]] * pow2[Size] % mod;
tag[root[u]] = 1ll * tag[root[u]] * pow2[Size] % mod;
ans[u] = (ans[u] + (sum1[root[u]] - sum2[root[u]]) % mod) % mod;
insert(root[u], 1, n, u, Size, 1ll * u * (pow2[Size] - 1) % mod, pow2[Size] - 1);
}
int main() {
n = read();
pow2[0] = inv2[0] = 1, pow2[1] = 2, inv2[1] = 499122177;
for (int i = 2; i <= n; ++ i)
AddEdge(read(), i), pow2[i] = (pow2[i - 1] << 1) % mod, inv2[i] = 1ll * inv2[i - 1] * inv2[1] % mod;
tot = 0, dfs(1);
for (int i = 1; i <= n; ++ i) printf("%d ", (ans[i] + mod) % mod);
return 0;
}
本文作者:zqs2020
本文链接:https://www.cnblogs.com/stinger/p/16749310.html
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步