你可见过一种基于状压的二进制筛法?

这个 trick 的妙,妙到让我愿意为它专门写一篇文章出来。(同时会写在 trick 里面)

基于状压,换成数学的语言,函数的值域是一个集合。比如说叫它 \(f(S)\)

然后这个 \(f\) 满足一些递推的性质:当我们知道 \(f(S)\) 的值的时候,可以顺便知道 \(f(S')\) 的值,并且这样的 \(S'\) 数量之和不会很多。

可以类比一下整数的筛法。在整数的筛法中,(多数是)我们可以由 \(f(x)\) 知道 \(f(xk)\) 的值(其中 \(k>1\)\(k\) 为整数),并且这样的 \(xk\) 的数量很少,是调和级数。

例题 10.16 B.竞赛图

给你一个竞赛图,求它有多少个诱导子图是强连通的。

我们知道竞赛图强连通分量缩点之后一定是一条链。那么,如果它不是一个强连通的图,那肯定可以划分成两部分 \(A,B\),使得 所有 边都是从 \(A\)\(B\) 的。

即,如果存在 \(A\)\(B\),使得 \(A\)\(B\) 的所有边都是出边,那 \(A|B\) 就没了。

\(f(S)\) 表示 \(S\) 是否“不是一个强连通分量” (类似质数筛,这么定义是方便初始化)

定义 “筛一遍” \(S\):我们找到 \(S\) 中所有点的 出边的交,设为 \(O\)(可以预处理);对于 \(O\) 的任意子集 \(T\)\(S|T\) 一定不是强连通图,令 \(f(S|T)=1\)

什么样的 \(S\) 需要像这样“筛一遍”呢?只有 \(f(S)=0\)\(S\) 需要来枚举一下这个 \(T\),否则 一定被更小的集合筛过了

就像做质数筛的时候只要用质数去更新筛即可。比如说,我们筛掉 \(6\) 的倍数的时候,在筛 \(2,3\) 的时候已经包含了这个操作,没必要再做一遍

关于复杂度: 我们发现一个集合 只会被筛到一次。那么我们就按照上面说的模拟,直接筛,时间复杂度就是 \(O(2^n)\) 的。

代码

posted @ 2020-10-16 14:18  Flandre-Zhu  阅读(106)  评论(4编辑  收藏  举报