[NOI2020] 命运

题面

题解

CF1327F AND Segments + 整体 dp。

首先预处理 \(\mathrm{pre}_i\) 表示向上最深的 \(f(e) = 1\) 的边的深度最小值。

\(f_{i, j}\) 表示当前在点 \(i\),最深的 \(f(e) = 1\) 的深度为 \(j\) 的方案数。

枚举点 \(i\) 和儿子之间的边是否设成 \(1\),有:

\[f_{i, j} = [\mathrm{pre}_i < j \leq \mathrm{dep}_i]\prod_{s \in \mathrm{son}(i)} (f_{s,j} + f_{s, \mathrm {dep}_s}) \]

其中 \(f_{s, \mathrm{dep}_s}\) 表示将 \(s\) 和它父亲的边设成 \(1\) 的方案数。

考虑用线段树合并维护,那么只需要维护区间加,区间乘和区间赋值,维护一个标记 \((a, b)\) 使得 \(f \stackrel{(a, b)}{\longrightarrow} af + b\)

手推一下标记如何合并即可。

代码

#include <cstdio>
#include <algorithm>
#include <vector>

inline int read()
{
	int data = 0, w = 1; char ch = getchar();
	while (ch != '-' && (ch < '0' || ch > '9')) ch = getchar();
	if (ch == '-') w = -1, ch = getchar();
	while (ch >= '0' && ch <= '9') data = data * 10 + (ch ^ 48), ch = getchar();
	return data * w;
}

const int N(5e5 + 10), Mod(998244353);
struct edge { int next, to; } e[N << 1];
int n, m, head[N], e_num, dep[N], pre[N], rt[N];
inline void add_edge(int from, int to)
	{ e[++e_num] = (edge) {head[from], to}, head[from] = e_num; }
void dfs(int x, int fa)
{
	dep[x] = dep[fa] + 1;
	for (int i = head[x]; i; i = e[i].next)
		if (e[i].to != fa) dfs(e[i].to, x);
}

int ls[N << 6], rs[N << 6], cur, pool[N << 6], top;
struct node { int a, b; } t[N << 6], I = (node) {1, 0};
inline node operator * (const node &x, const node &y)
	{ return (node) {1ll * x.a * y.a % Mod, (y.b + 1ll * x.b * y.a) % Mod}; }
inline int operator == (const node &x, const node &y) { return x.a == y.a && x.b == y.b; }
int newNode(const node &v)
{
	int x = top ? pool[top--] : ++cur;
	return ls[x] = rs[x] = 0, t[x] = v, x;
}

void pushdown(int x)
{
	if (t[x] == I) return;
	if (!ls[x]) ls[x] = newNode(t[x]); else t[ls[x]] = t[ls[x]] * t[x];
	if (!rs[x]) rs[x] = newNode(t[x]); else t[rs[x]] = t[rs[x]] * t[x];
	t[x] = I;
}

void Modify(int x, int ql, int qr, const node &v, int l = 1, int r = n)
{
	if (ql > qr) return;
	if (ql <= l && r <= qr) return (void) (t[x] = t[x] * v);
	int mid = (l + r) >> 1; pushdown(x);
	if (ql <= mid) Modify(ls[x], ql, qr, v, l, mid);
	if (mid < qr) Modify(rs[x], ql, qr, v, mid + 1, r);
}

int Query(int x, int p, int l = 1, int r = n)
{
	if (l == r) return t[x].b;
	int mid = (l + r) >> 1; pushdown(x);
	if (p <= mid) return Query(ls[x], p, l, mid);
	else return Query(rs[x], p, mid + 1, r);
}

int merge(int &x, int &y)
{
	if (!ls[x] && !rs[x]) std::swap(x, y);
	if (!ls[y] && !rs[y])
		return t[x] = t[x] * (node) {t[y].b, 0}, x;
	pushdown(x), pushdown(y);
	ls[x] = merge(ls[x], ls[y]);
	rs[x] = merge(rs[x], rs[y]);
	return x;
}

void clear(int &x) { if (!x) return; clear(ls[x]), clear(rs[x]), pool[++top] = x, x = 0; }
void dp(int x, int fa)
{
	pre[x] = std::max(pre[x], pre[fa]);
	rt[x] = ++cur, t[cur] = (node) {0, 0}, Modify(cur, pre[x] + 1, dep[x], (node) {0, 1});
	for (int i = head[x]; i; i = e[i].next) if (e[i].to != fa)
		dp(e[i].to, x), rt[x] = merge(rt[x], rt[e[i].to]), clear(rt[e[i].to]);
	if (x != 1) Modify(rt[x], 1, dep[x] - 1, (node) {1, Query(rt[x], dep[x])});
}

int main()
{
	n = read();
	for (int i = 1, a, b; i < n; i++)
		a = read(), b = read(), add_edge(a, b), add_edge(b, a);
	m = read(), dfs(1, 0);
	for (int i = 1, x, y; i <= m; i++)
		x = read(), y = read(), pre[y] = std::max(pre[y], dep[x]);
	dp(1, 0), printf("%d\n", Query(rt[1], 1));
	return 0;
}
posted @ 2020-08-20 14:32  xgzc  阅读(850)  评论(0编辑  收藏  举报