这个 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)) 的。