NOI 2020 D1T2 Destiny
Task
给定 \(n\) 个点的以 \(1\) 为根的有根树,有 \(m\) 条约束,每条约束包含一个点对 \((u,v)\),满足 \(u\) 是 \(v\) 的祖先,你需要给每条边染成黑白两种颜色,满足对于每条约束,\(u\rightarrow v\) 的路径上都有一条黑边,求合法方案数。
答案对 \(998244353\) 取模。
限制:\(1\le n,m\le 5\times10^5\)
Solution
预处理 \(\text{limit}[u]\) 表示 \(u\) 往上的黑边中,最近的黑边的深度需要大于等于 \(\text{limit}[u]\)。这里的 \(\text{limit}\) 仅考虑以点 \(u\) 为下端点的限制。
用 \(\text{parent[u]}\) 来表示 \(u\) 的父亲结点,设 \(\text{dp}[u][d]\) 表示 \(\text{parent}[u]\) 往上最近的黑边的深度为 \(d\) 的方案数。
设 \(\mathcal V\) 表示点 \(u\) 的儿子集合。
接下来考虑求出 \(\text{dp}[u][d]\),根据边 \((\text{parent}[u],u)\) 染色的情况分类讨论:
\[\text{dp}[u][d]=[d\ge\text{limit[u]}]\prod_{v\in\mathcal V}\text{dp}[v][d]+\prod_{v\in \mathcal V} \text{dp}[v][\text{dep}[u]] \]前者是染白的方案数,后者是染黑的方案数。
然后我们考虑对树上的每一个结点开一棵线段树维护 \(\text{dp}\) 数组,具体来说,结点 \(u\) 所对应的线段树的 \([d,d]\) 区间维护了 \(\text{dp}[u][d]\) 的值。
然后我们考虑 \(\text{dp}\) 的转移是如何体现在线段树上的操作的:
- 我们将线段树的 \(\text{limit}[u]\sim n\) 的位置区间加为 \(1\),表示这些位置是可以被染成白边的。
- 我们将 \(u\) 的所有儿子对应的线段树合并起来,也就是对应位置的值相乘,这一步是为了计算染成白边的方案数。
- 接下来我们查询这棵树上 \(\text{dep}[u]\) 对应的值,全局加上这个值。
总结一下,我们的线段树需要支持
- 区间加
- 单点查询
- 区间对应位置乘
这里需要使用到线段树合并 pushdown
的 trick,只要该点没有左右儿子,就不需要 pushdown
。
什么时候,我们才能真正决定自己的命运呢?