NOI 2020 D1T2 命运(Destiny) Solution

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}\) 的转移是如何体现在线段树上的操作的:

  1. 我们将线段树的 \(\text{limit}[u]\sim n\) 的位置区间加为 \(1\),表示这些位置是可以被染成白边的。
  2. 我们将 \(u\) 的所有儿子对应的线段树合并起来,也就是对应位置的值相乘,这一步是为了计算染成白边的方案数。
  3. 接下来我们查询这棵树上 \(\text{dep}[u]\) 对应的值,全局加上这个值。

总结一下,我们的线段树需要支持

  1. 区间加
  2. 单点查询
  3. 区间对应位置乘

这里需要使用到线段树合并 pushdown 的 trick,只要该点没有左右儿子,就不需要 pushdown

什么时候,我们才能真正决定自己的命运呢?

posted @ 2021-10-17 01:25  feicheng  阅读(86)  评论(0编辑  收藏  举报