1.9日闲话:DAG 计数
本人以后的学习笔记会尽量以闲话形式发出,避免对初学者造成困惑。
超前情提要:jijidawang 和 xrlong 强烈推荐主旋律。
前情提要:吃饭的时候,bjt 和 zxk 让我写主旋律,我果断接受了学习这道题的任务(
但是由于抄了好几天的题解了,于是自己手推了一下,和自己搏斗了 INF 年,最终成功击败主旋律,写此题解纪念一下。
DAG 容斥
不是,这东西到底有啥用啊?上不如集合划分容斥,下不如子集反演。
直接上子集反演推法了。
由于一个 DAG 具有良好的递归子问题性质,假设现在有一个 DAG,那么删除所有入度为 0 的点后还是一个 DAG,可以发现如果这样构造出 DAG 的话是不重不漏的。
下文的零度点指的就是入度为 0 的点。
那么有一个简单的思路就是枚举所有的零度点转移,剩下的单独构成一个 DAG,列出简单的一个式子,假设 \(v(S, T)\) 表示从集合 \(S\) 到集合 \(T\) 的连边方案数,那么有:
\[f_S = \sum_{\varnothing \subset T \subseteq S}v(T, S - T)f_T \]
显然我们的转移不可能从滚木集合转移过来。
其中 \(S - T\) 表示对称差。
可是这样实在是有点自欺欺人了,我们发现一件惊恐的事情,就是 \(T\) 里也可能有零度点。
假设现在处在 \(S\) 这个大的点集之下考虑,设 \(g_T\) 表示恰好 \(T\) 为零度点的方案数,\(h_T\) 表示钦定 \(T\) 为零度点的方案数。
由于:
\[h_T = \sum_{T \subseteq U \subseteq S}{g_U} = v(T, S - T)f_{S - T} \]
通过子集反演可得:
\[g_T = \sum_{T \subseteq U \subseteq S}{(-1)^{|U| - |T|}h_U} = \sum_{T \subseteq U \subseteq S}{(-1)^{|U| - |T|}v(U, S - U)f_{S - U}} \]
然后枚举一下零度点再推推式子就得到了:
\(\begin{aligned} f_S &= \sum_{\varnothing \subset T \subseteq S}{\sum_{T \subseteq U \subseteq S}{(-1)^{|U| - |T|}v(U, S - U)f_{S - U}}} \\ &= \sum_{\varnothing \subset U \subseteq S}v(U, S - U)f_{S - U}\sum_{\varnothing \subset T \subseteq U}{{(-1)^{|U| - |T|}}} \\ &= \sum_{\varnothing \subset U \subseteq S}v(U, S - U)f_{S - U}(-1)^{|U|}(\sum_{T \subseteq U}{{(-1)^{|T|}}} - 1) \\ &= \sum_{\varnothing \subset T \subseteq S}(-1)^{|T| + 1}v(T, S - T)f_{S - T} \end{aligned} \)
这就是经典的 DAG 容斥系数 \((-1)^{|T| + 1}\) 的一种推法了。
个人感觉对于这类问题是多变的,所以会一个好理解的简单做法现场重新推一遍是最好的,就比如下面的主旋律,其形式并不相同。
比起一些需要理解的做法,这个肯定是很简单的了。
我们可以追溯一下本质,其实这个 \(T\) 还是在钦定那些点作为零度点,所以其实还是需要乘上一个 \(w_T\) 表示零度点之间的贡献,不过现在讨论的阶段不存在这个系数。
Amusement Park
这题其实是需要一步转化的,就是发现对于一个合法的定向方式,其全部翻转后也是合法的,那么所有方案取平均值后就是方案数乘上 \(\frac{m}{2}\)。
其实上面那步是最难的。。。
然后就是板子了,发现 \(v(S, T)\) 其实是 1,因为已经定好向了。
然后 \(w_T\) 或者说是如果这个枚举的 \(T\) 不是一个独立集那就不能转移。
有标号 DAG 计数
注:含多项式内容。
首先要求弱联通,这点很没用,最后对 EGF 上发多项式 ln 即可。
假设 \(f_i\) 表示 \(i\) 个点构成的 DAG 方案数。
其中可以得到 \(v(S, T) = 2^{|S||T|}\)
套用上面的式子得到:
\[f_i = \sum_{j = 1}^{i}\binom{i}{j}f_{i - j}2^{ij} \]
这其实本质是将 \(f_S\) 压缩到了 \(f_{|S|}\) 这个状态里。
看着很想半在线卷积,不过还差点。
由于:\(ij = \binom{i + j}{2} - \binom{i}{2} - \binom{j}{2}\)
\[f_i = \sum_{j = 1}^{i}{\frac{i!}{j!(i - j)!}f_{i - j}\frac{2^{\binom{i}{2}}}{2^\binom{j}{2}2^\binom{i - j}{2}}} \]
更改一下就出现卷积形式了:
\[\frac{f_i}{i!2^{\binom{i}{2}}} = \sum_{j = 1}^{i}{\frac{1}{j!2^{\binom{j}{2}}}\frac{f_{i - j}}{(i - j)!2^{\binom{i - j}{2}}}} \]
现在得到了这样的形式:
\[F_i = \sum_{j = 1}^{i}{G_jF_{i - j}} \]
其中 \(G_0 = 0\) 。
发现 \(F * G\) 卷积之后只有 \(x^0\) 次项系数是错的,加上就行了。
\[F = F * G + 1 \]
\[F = \frac{1}{1 - G} \]
最后 ln 成答案就行。
复杂度 \(O(n\log n)\)。
主旋律
今日主角。
首先先不要慌,如果一个图不是强连通图,那么其一定是由一堆强连通分量构成的大于一个点的 DAG。
于是乎变成了 DAG 计数。
现在假设 \(f_S\) 表示 \(S\) 构成的方案数, \(a_S\) 表示 \(S\) 构成的强连通图个数。
此时转移时 \(v(S, T) = 2^{e(S, T)}\) 其中 \(e(S, T)\) 表示从 \(S\) 指向 \(T\) 的边数。
但此时 \(w_T\) 是有意义的了,因为前面说的那些都是零度点,可这里不一样了,这些是没有入度的强连通分量,所以假设我们现在钦定一堆强连通分量作为零度集团,那么可以自己推一下,根据子集反演可得:如果钦定了 \(cnt\) 个强连通分量,那么容斥系数应当是 \((-1)^{cnt + 1}\)。
如果先不管容斥系数的话,我们的 \(w_T\) 也是有良好的递归结构的,就是枚举新的强连通分量由那些点构成,但是这样会算重,所以需要钦定最小编号的点在新加入的强连通分量中。
而容斥系数有什么含义呢,其实就是我们现在新加入一个强连通分量,那么其贡献应当为:\(-1\) 而非 \(+1\)。
式子就是:
\[w_S = \sum_{\varnothing \subset T \subseteq S}{-a_Tw_{S - T}} \]
然后是 \(f_S\) 如何求得,由于容斥系数已经算过了,那么直接套用之前的式子:
\[f_S = \sum_{\varnothing \subset T \subseteq S}{w_Tf_{S - T}2^{e(T, S - T)}} \]
\[a_S = 2^{e(S, S)} - f_S \]
不过这里有一个小问题,就是 \(f_S\) 和 \(w_S\) 冲突了,但是简单调整一下发现,\(w_S\) 需要的 \(f_S\) 其实是 \(a_S\) 单独形成一个强连通分量,这是不能转移到 \(f_S\) 里的。
于是解决方案就是先不转移 \(f_S\) 对 \(w_S\) 的贡献,算完 \(f_S\) 后再算 \(w_S\)。
不过这复杂度是 \(O(3^nn^2)\) 的。
然后发现一点,就是 \(e(S, T)\) 其实等于:
\[e(S, T) = \sum_{i \in S}{e(\{i\}, T)} \]
然后预处理一下 \(e(\{i\}, T)\) 即可。
精细实现复杂度变成 \(O(3^nn)\) 或 \(O(3^n)\)。