参考: 王迪2013年国家集训队论文浅谈容斥原理
这篇论文后面可能会完整地学习一次, 写一篇学习笔记. 但至少这篇文章里面只是涉及到其中的一些内容, 一些原理性的东西可能就不求甚解了.
预备知识: 容斥原理的一般化
对于两个关于集合的函数\(f(S)\)和\(g(S)\), 假如有
\[f(S) = \sum_{T \subseteq S} g(T)
\]
那么就有
\[g(S) = \sum_{T \subseteq S} (-1)^{|S| - |T|} f(T)
\]
证明这里无法给出.
我们还可以对其稍作变形, 无非就是把\(\subseteq\)改成\(\supseteq\), 这里不再赘述.
带标号的DAG计数
这里开始进入正题了.
我们给定一个\(n\), 对\(n\)个点的有标号的有向无环图(DAG)进行计数, 答案模\(10^9 + 7\). 数据规模满足\(n \le 5 \times 10^3\).
考虑动态规划. 注意到有向无环图中必然存在一些出度为零的点, 因此我们用\(f(i, j)\)表示总共有\(i\)个点的有向无环图, 并且满足出度为零的点的数量为\(j\)的DAG数量, 则有递归式
\[f(i, j) = \left(\begin{array}{} j \\ i \end{array}{} \right) \sum_{k = 1}^{i - j} (2^j - 1)^k 2^{j(i - j - k)} f(i - j, k)
\]
分析这则式子的含义: \(\left( \begin{array}{} i \\ j \end{array}{} \right)\)表示在\(i\)个点中任意选出\(j\)个点的情况数; \(\sum_{k = 1}^{i - j}\)表示枚举去掉这\(k\)个点后的图有多少个点出度为\(0\); \((2^j - 1)^k\)表示原图中这些出度为\(0\)的点至少要与新图中的\(k\)个点连一条边, 否则其出度还是为\(0\); \(2^{j(i - j - k)}\)显而易见, 不解释了; \(f(i - j, k)\)递归上一层图的情况. 时间复杂度: \(O(n^3)\).
考虑是否有复杂度更优的算法? 注意到我们在定义\(f(i, j)\)时使用的是"恰好有\(j\)个点出度为零", 考虑修改这个限制: 我们令\(g(i, S)\)表示总共\(i\)个点, 且\(S\)这个集合中的点出度为零, \(h(i, j)\)表示总共\(i\)个点, 且至少有\(S\)这个集合中的点出度为零. 那么我们可以得到如下递归式:
\[h(i, S) = 2^{(i - |S|)|S|} h(i - |S|, \emptyset)
\]
再考虑\(g\)和\(h\)的关系:
\[h(i, S) = \sum_{T \supseteq S} g(i, T) \\
g(i, S) = \sum_{T \supseteq S} (-1)^{|S| - |T|} h(T)
\]
然而我们的目的在于求\(h(n, \emptyset)\), 因此有
\[\begin{aligned}
h(n, \emptyset) &= \sum_{T \ne \emptyset} \text{\\ 为什么T不能为空集? 因为DAG中必定存在出度为0的点} \\
&= \sum_{m = 1}^n \sum_{|T| = m} f(n, T)
\end{aligned}
\]
再代入之前推导出的
\[g(i, S) = \sum_{T \supseteq S} (-1)^{|S| - |T|} h(T)
\]
我们有
\[\begin{aligned}
h(n, \emptyset) &= \sum_{m = 1}^n \sum_{|T| = m} \sum_{S \supseteq T} (-1)^{|S| - |T|} g(n, S) \\
&= \sum_{m = 1}^n \sum_{|T| = m} \sum_{S \supseteq T} (-1)^{|S| - |T|} 2^{(n - |S|)|S|} h(n - |S|, \emptyset) \\
&= \sum_{m = 1}^n \sum_{|T| = m} \sum_{k = m}^n \left( \begin{array}{} n - m \\ k - m \end{array}{} \right) (-1)^{k - m} 2^{(n - k)k} h(n - k, \emptyset) \\
&= \sum_{m = 1}^n \left( \begin{array}{} n \\ m \end{array}{} \right) \sum_{k = m}^n \left( \begin{array}{} n - m \\ k - m \end{array}{} \right) (-1)^{k - m} 2^{(n - k)k} h(n - k, \emptyset)
\end{aligned}
\]
到这里好像就没法继续化简了. 其实不然.
\[\begin{aligned}
&\ \sum_{m = 1}^n \left( \begin{array}{} n \\ m \end{array}{} \right) \sum_{k = m}^n \left( \begin{array}{} n - m \\ k - m \end{array}{} \right) (-1)^{k - m} 2^{(n - k)k} h(n - k, \emptyset) \\
&= \sum_{k = 1}^n \sum_{m = 1}^k \left( \begin{aligned}{} n \\ m \end{aligned} \right) \left( \begin{array}{} n - m \\ k - m \end{array}{} \right) (-1)^{k - m} 2^{(n - k)k} h(n - k, \emptyset) \\
&= \sum_{k = 1}^n 2^{k(n - k)} h(n - k, \emptyset) \sum_{m = 1}^k (-1)^{k - m} \left( \begin{aligned}{} n \\ m \end{aligned} \right) \left( \begin{array}{} n - m \\ k - m \end{array}{} \right) \\
\end{aligned}
\]
考虑如何处理上面的组合数: 我们有如下结论
\[\left( \begin{aligned}{} n \\ m \end{aligned} \right) \left( \begin{array}{} n - m \\ k - m \end{array}{} \right) = \left( \begin{array}{} n \\ k \end{array} \right) \left( \begin{array}{} k \\ m \end{array} \right)
\]
我们有
\[\begin{aligned}
&\ \sum_{k = 1}^n 2^{k(n - k)} h(n - k, \emptyset) \sum_{m = 1}^k (-1)^{k - m} \left( \begin{aligned}{} n \\ m \end{aligned} \right) \left( \begin{array}{} n - m \\ k - m \end{array}{} \right) \\
&= \sum_{k = 1}^n 2^{k(n - k)} h(n - k, \emptyset) \left( \begin{array}{} n \\ k \end{array} \right) \sum_{m = 1}^k (-1)^{k - m} \left( \begin{array}{} k \\ m \end{array} \right) \\
&= \sum_{k = 1}^n 2^{k(n - k)} h(n - k, \emptyset) \left( \begin{array}{} n \\ k \end{array} \right)[(1 - 1)^k - 1^0 (-1)^k] \text{ 根据二项式定理变形} \\
&= - \sum_{k = 1}^n 2^{k(n - k)} h(n - k, \emptyset) \left( \begin{array}{} n \\ k \end{array} \right)(-1)^k \\
&= \sum_{k = 1}^n (-1)^{k - 1} \left( \begin{array}{} n \\ k \end{array} \right) 2^{k(n - k)} h(n - k, \emptyset)
\end{aligned}
\]
这样一来, 我们就把计算的时间复杂度降低到了\(O(n^2)\), 问题解决.