0928模拟赛 树形熔断器

在计算以 \(u\) 为根的子树的答案时,只需要知道会经过 \(u\) 的方案的和,再加上儿子的答案。

对于子树内的点 \(v\),假设它是 \(u\)\(son\) 方向的儿子,要让它成为最小值,由于算的是经过 \(u\) 的方案,那么 \(v\rightarrow u\) 上的所有点都会成为这种方案经过的点。因此 \(v\) 必须大于所有这些点。称 \(v\) 为关键点,定义一个关键点 \(v\) 的管辖范围为:以 \(v\) 为根的子树减去所有离他最近的(即到 \(v\) 的路径上没有其它关键点的)关键点为根的子树。它的 \(size\) 定义为管辖范围大小。

假设我们已经抓出了这些点 \(v\),考虑如何计算方案数。

要让 \(v\) 成为最小值,只能选权值(即编号)大于它的关键点的的管辖范围,而且还必须选一个除了 \(son\) 子树内以外的 \(u\) 子树内的点。不难想到先计算不管“必须选一个除了 \(son\) 子树内以外的 \(u\) 子树内的点”的限制的方案,再减去只选 \(son\) 子树内的点的方案即可。令权值大于 \(v\) 的关键点的 \(size\) 之和为 \(suf\),所有方案的值总和显然为 \((2^{size}-1)(2^{suf}-1)v\)。据此可以计算答案。

现在考虑如何抓出所有关键点。使用线段树合并,假设当前要和 \(son\) 合并,那么先删掉 \(son\) 内所有权值大于 \(u\) 的关键点。这不会影响其它点的管辖范围,因为一个关键点的后代关键点权值肯定比它要小。然后再减去只选 \(son\) 子树内的点的方案,再合并到 \(u\) 的线段树上。注意以 \(u\) 为最小值的情况需要单独计算。上面那个算答案的式子需要拆成两项维护,每个节点还要维护 \(size\) 和,所以总共要维护三个信息。因为删掉的关键点是一段后缀,所以只需要删掉后整体打个标记除以 \(2\) 的删掉的部分的 \(size\) 和次方即可。注意合并时 \(suf\) 也会变,需要更新。

时间复杂度 \(\Theta(n\log n)\)

比树剖套线段树套矩阵的垃圾官方题解不知道高到哪里去了

#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;
}
posted @ 2022-10-02 19:44  zqs2020  阅读(43)  评论(0编辑  收藏  举报