• 带标号的各种图计数学习笔记


    参考: 王迪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)), 问题解决.

  • 相关阅读:
    AngularJs之ng-repeat的用法
    JavaBean转换为XML的源码
    JS插件之——ztree
    JS插件之——bootstrap-suggest.js
    oracle异常记录
    Java 中常用缓存Cache机制的实现《二》
    缓存、缓存算法和缓存框架简介
    oracle数据库高级应用之《自动生成指定表的insert,update,delete语句》
    Oracle语句优化之一
    android 7.0 多渠道打包
  • 原文地址:https://www.cnblogs.com/ZeonfaiHo/p/7396969.html
Copyright © 2020-2023  润新知