参考: 王迪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 imes 10^3).
考虑动态规划. 注意到有向无环图中必然存在一些出度为零的点, 因此我们用(f(i, j))表示总共有(i)个点的有向无环图, 并且满足出度为零的点的数量为(j)的DAG数量, 则有递归式
[f(i, j) = left(egin{array}{} j \ i end{array}{}
ight) sum_{k = 1}^{i - j} (2^j - 1)^k 2^{j(i - j - k)} f(i - j, k)
]
分析这则式子的含义: (left( egin{array}{} i \ j end{array}{}
ight))表示在(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)), 因此有
[egin{aligned}
h(n, emptyset) &= sum_{T
e emptyset} ext{\ 为什么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)
]
我们有
[egin{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( egin{array}{} n - m \ k - m end{array}{}
ight) (-1)^{k - m} 2^{(n - k)k} h(n - k, emptyset) \
&= sum_{m = 1}^n left( egin{array}{} n \ m end{array}{}
ight) sum_{k = m}^n left( egin{array}{} n - m \ k - m end{array}{}
ight) (-1)^{k - m} 2^{(n - k)k} h(n - k, emptyset)
end{aligned}
]
到这里好像就没法继续化简了. 其实不然.
[egin{aligned}
& sum_{m = 1}^n left( egin{array}{} n \ m end{array}{}
ight) sum_{k = m}^n left( egin{array}{} n - m \ k - m end{array}{}
ight) (-1)^{k - m} 2^{(n - k)k} h(n - k, emptyset) \
&= sum_{k = 1}^n sum_{m = 1}^k left( egin{aligned}{} n \ m end{aligned}
ight) left( egin{array}{} n - m \ k - m end{array}{}
ight) (-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( egin{aligned}{} n \ m end{aligned}
ight) left( egin{array}{} n - m \ k - m end{array}{}
ight) \
end{aligned}
]
考虑如何处理上面的组合数: 我们有如下结论
[left( egin{aligned}{} n \ m end{aligned}
ight) left( egin{array}{} n - m \ k - m end{array}{}
ight) = left( egin{array}{} n \ k end{array}
ight) left( egin{array}{} k \ m end{array}
ight)
]
我们有
[egin{aligned}
& sum_{k = 1}^n 2^{k(n - k)} h(n - k, emptyset) sum_{m = 1}^k (-1)^{k - m} left( egin{aligned}{} n \ m end{aligned}
ight) left( egin{array}{} n - m \ k - m end{array}{}
ight) \
&= sum_{k = 1}^n 2^{k(n - k)} h(n - k, emptyset) left( egin{array}{} n \ k end{array}
ight) sum_{m = 1}^k (-1)^{k - m} left( egin{array}{} k \ m end{array}
ight) \
&= sum_{k = 1}^n 2^{k(n - k)} h(n - k, emptyset) left( egin{array}{} n \ k end{array}
ight)[(1 - 1)^k - 1^0 (-1)^k] ext{ 根据二项式定理变形} \
&= - sum_{k = 1}^n 2^{k(n - k)} h(n - k, emptyset) left( egin{array}{} n \ k end{array}
ight)(-1)^k \
&= sum_{k = 1}^n (-1)^{k - 1} left( egin{array}{} n \ k end{array}
ight) 2^{k(n - k)} h(n - k, emptyset)
end{aligned}
]
这样一来, 我们就把计算的时间复杂度降低到了(O(n^2)), 问题解决.