集合幂级数在子图计数上的应用
定义与基本运算
和生成函数差不多,把指数上的东西换成集合,可以这样简单理解集合幂级数。
需要定义运算,加法很简单,指数相同的,系数相加。
乘法定义为指数上的无交并的积之和:\([x^s](f* g)=\sum\limits_{t \subset s}[x^t]f \times [x^{s/t}]g\)
快速计算集合幂级数的乘法,其实也就是子集卷积。
如果直接写或 FWT 是不对的,会多算,我们再引入一个占位元 \(y\),把无交并可化为:\(s|t=u,|s|+|t|=|u|\),这个条件是冲要的。
换成形式化的就是 \([x^s y^{|s|}](f*g)=\sum\limits_{t \subset s}[x^t y^{|t|}]f \times [x^{s/t} y^{|s|-|t|}]\)
你可以理解成原本的 FWT 中都是对整数进行的运算,现在把整数换成多项式,FWT 中只有加减法,因此多项式每一位独立。
先做 FWT,这样对于 \(x\) 的乘法全部为对位乘法,不同指数间相互独立,然后对于 \(y\) 做正常的多项式乘法即可。
函数复合
函数的复合主要是求逆,\(\operatorname{exp,ln}\)。
具体来说设 \(F(x)\) 是一个集合幂级数,我们需要求出来 \(\frac{1}{F(x)},e^{F(x)},ln(F(x))\)。
因为 \(x\) 元上的集合卷积很麻烦,我们直接把 \(F(x)\) 变换成 \(\operatorname{FWT}(F(x))\),之后我们只需要考虑每一位上的普通多项式的运算。
由于 \(n\) 很小,我们只需要 \(n^2\) 算上面的三种运算。
以下简写 \([x^n]F(x)\) 为 \(F_n\)。
求逆:
两边系数拿出来作比较,设为 \([x^n]\):
容易写出递推式:
\(\operatorname{Exp}\):
两边同时求导:
只需要多项式乘法,然后再积分回去就好了。
不过可以通过比较系数法一步到位,比较两边的 \([x^n]\):
写出递推式:
好看一点,将 \(i\) 换成 \(n-i\)
\(\operatorname{Ln}\):
两边同时求导:
一个显然的做法是先求逆做多项式乘法再积分回去。
比较系数法仍然是可以获得更小常数的递推做法:
化为 \(B'(x)A(x)=A'(x)\),比较 \([x^n]\):
好看一点,求和部分用 \(i\) 替换 \(i+1\):
默认 \(A_0=1\),因此可以直接写为:
以上复杂度均为 \(O(2^n n^2)\),如果不需要,则可以直接拿子集枚举的式子,然后求逆用比较系数法,\(\operatorname{exp}\) 和 \(\operatorname{ln}\) 用组合意义就可以了,不再赘述。
常见复合的组合意义:
\(\operatorname{exp}\):
\(\operatorname{exp}\) 的展开式为 \(\sum\limits_{i \ge 0}\frac{F^i(x)}{i!}\)。
\(F^i(x)\) 在集合幂级数中表示 \(i\) 个不交集合权值的乘积,而除以 \(i!\) 表示去掉方案。
则组合意义为由若干个无序不交集合组合而成,其中“组合”指的是权值的乘积。
\(\operatorname{ln}\):
\(\operatorname{ln}\) 作为 \(\operatorname{exp}\) 的逆运算,其组合意义就是将某个集合拆解为若干无序不交集合,这些集合的乘积为当前集合的权值。
更具体的意义在例题中体现:
半在线改造:
这个其实是容易的,因为我们只会从集合大小小的向大的转移。
所以我们求占位多项式的第 \(i\) 位的时候,只会用到 \(i-1\) 以下位的信息,这样我们就算出来了在线数组 \(\operatorname{FWT}f_i\)。
应用:
联通性限制:
思路:直接算不要求联通的方案数,然后尝试用联通的通过 \(\operatorname{exp}\) 组合出任意的方案数,这样我们求 \(\operatorname{ln}\) 就好了。
联通子图计数:
你学的容斥方法的本质是 ln。
设 \(F(x)\) 表示联通生成子图的集合幂级数,我们发现对于所有图形我们都可以用若干联通子图来表示,且不同联通子图互不影响,具体来说设 \(G(x)\) 表示原图生成子图的集合幂级数,则 \(G(x)=\operatorname{exp}(F(x))\)。
\(G(x)\) 是容易的,\([x^s]G(x)=2^{\sum\limits_{(u,v) \in E}[u,v \in s]}\)。
直接求\(\operatorname{ln}\) 就好了。
练习:
-
「雅礼集训 2019 Day8」union
模板题,联通图权值 exp = 任意图权值。直接求 \(\operatorname{ln}\) 即可。 -
你学的容斥是在暴力求 ln一个想法是枚举黑点和百点的点集,要求内部没有边,中间随便连。但是这样并没有考虑联通的限制,现设生成的集合幂级数为 \(G(x)\)。思考这样算出来的到底是什么。
其实是原图所有生成二分子图黑白染色的方案数量。我们仍然尝试用联通块的组合来描述。设 \(F(x)\) 为联通生成二分子图的集合幂级数,其中系数为方案数。注意到若一个子图有 \(k\) 个联通块,则黑白染色的方案数应该是 \(2^k\),因此我们给 \(F(x)\) 乘二再\(\operatorname{exp}\)起来,就可以得到 \(G(x)\),我们直接把 \(G(x)\) 求 \(\operatorname{ln}\) 再除二就好了。
-
欧拉回路的限制为所有点度数均为偶数。容易发现联通欧拉子图 \(\operatorname{exp}\) 后就变成了不联通的。
快速求解不联通的:抽象为:有多少种选边的方案,满足边的异或为 0。可以线性基解决,把点集内的边拉出来插到线性基里面,总数-秩的个数的二的次幂就是答案。
然后 \(\operatorname{ln}\) 回去就好了。
DAG 上:
思路:枚举没有入度的部分(缩点后),赋权容斥系数 \((-1)^{k+1}\),得到一个子集枚举形式的恒等式。
DAG 定向计数:
DAG 是具有子结构的东西:去掉 0 入度的点后,剩下的部分仍然是一个 DAG。
考虑枚举 0 入度点的集合,要求这部分是个独立集。但是发现一个 DAG 中的零入度点集合会被所有非空子集枚举到,因此设 \((-1)^|s|\) 为容斥系数可以保证只算一次。
形式化:\(f_s=\sum\limits_{t \subset s,t \not= \emptyset}(-1)^{|t+1|}f_{s/t}, t 为独立集\)
然后发现其实就是设一个集合幂级数 \(F(x)\),其中每项的系数是 \((-1)^{|s|}\),由于这个你每次扣掉的独立集之间是有顺序的,所以并不是\(\operatorname{exp}\),而是直接 \(\sum\limits_{i \ge 0}F^{i}(x)\),这个东西收敛后是 \(\frac{1}{1-F(x)}\),集合幂级数求逆就行了。
强连通子图计数:
一张有向图,缩点后就是 DAG。按照思路走,我们枚举缩完点后的0入度强连通分量集合 \(S\),他的贡献应该是枚举分解的形态:\(G_S=\sum\limits_{T_1,T_2,……,T_k}(-1)^k \prod\limits_{i=1}^{k}f_{T_i}\)。这些强连通分量之间没有边,然后这部分和剩下的点之间的边必须是顺序边,剩下点内部边可以任意。这样算的是整张图随便连的个数。
我们设 \(cross(S,T)\) 表示 \(S \rightarrow T\) 有向边的数量,用形式化的语言描述上面的东西就是:
观察这个 \(G\),发现他是无序组合的形式,用生成函数描述就是 \(G(x)=\sum\limits_{i \ge 0}\frac{(-1)^i \times f^i(x)}{i!}=\operatorname{exp}(-f(x))\)。
换句话说 \(-f(x)=\operatorname{ln}(g(x))\),我们的目标是求出 \(g\)。这个包含 \(g\) 的等式是个子集卷积,但是系数却是不独立的,只能暴力做了。直接比较系数法就行了。
与之相匹配的,我们也只需要暴力求 \(\operatorname{ln}\) 就好了。
练习:
-
经典!
把整个问题拆解:- 如果确定了拆开的段数,有多少种方法拆到这个段数。
- 有多少种拆成固定段的满足题目要求的形态。
先解决第一个问题,相对来说比较简单,由于段之间可以重拍,因此非空段间有顺序,空段间没顺序。于是先乘上 \(i!\),然后再和剩下的 \(k+1-i\) 个空段归并:\(\binom{k+1}{i}\)。最后我们任意顺序切他们中间的 \(k\) 个空当:\(k!\)。
刻画满足要求的形态:
- 段内为一个合法的拓扑序
- 短间不存在环:段间构成一张 DAG。
第一个问题乱做即可,第二个问题可以使用经典 DAG 计数技巧,这个做卷积的时候涉及到很多麻烦的东西,只能枚举子集。
然后记录个数的 \((+,\times)\) 可以利用拉差差系数做出来。
子结构分步拼接:
思路:
考虑图形的子结构,并且找到足够简单的组成图形的元素。得到这部分的集合幂级数后,逐步把子结构拼成最终的形态
树/森林生成子图计数
如果会做树的,森林直接 exp 就好了。
树的最简子结构是单个的点,没有比这个更简单的了。
设 \(p_u\) 表示当前树内所有点编号不超过 \(u\) 的集合幂级数。
每次相当于向 \(u\) 连边,可以先让 \(q_{u-1}=p_{u-1} \times w(u,*)\) 然后 \(p_u=exp(q_{u-1}) \cup \{u\}\)。
这个复杂度是很好的,因为我们每次只利用了前 \(i\) 个点的信息,所以复杂度为 \(O(2^{i} \times i^2)=O(2^{n} n^2)\)
如果你把最简子结构换成边,那么中间的部分可以直接 exp 而不需要变换,但是这样复杂度是 \(O(2^n n^3)\) 的。
仙人掌生成子图计数
这个反着考虑就没有意义了,因为联通图并不能由仙人掌组合而成。
因此我们考虑正着做,那么正着做我们需要的是找到子结构。
仙人掌缩点后是一颗园方树,每一个环(包括二元环)对应一个方点,非叶子原点连接若干个方点。
方点的编号是未定义的,考虑限制整张图非叶子原点的编号 \(\le u\),称此时联通仙人掌的集合幂级数为 \(p_u\)。
一开始的时候,不能有非叶子原点,因此只能是一个环。环的集合幂级数可以简单 dp 得出。
考虑 \(p_{u-1} \rightarrow p_{u}\),增加了 \(u\) 作为非叶子原点的部分,那么他一定连接若干个方点对应的仙人掌,这些仙人掌的贡献形式为 “无序不交组合”,我们直接做 \(\operatorname{exp}\) 即可得到,注意和点双的做法一样,需要合并的仙人掌包含 \(u\)。
练习:
-
如果没有限制,显然最简子结构是一条链。
然后有限制相当于现做一个子集卷积,不过好在只求并为全集的,可以快速解决。
双联通限制:
思路:
处理这个的常用手段是 点/边双联通-联通 变换,名字很炫酷,实际也很炫酷。
尝试反过来解决问题:我们把最简子结构视为"双连通图",最终的结构视为连通图。
也就是说:假设我们已经知道了 \(f(x)\) 为双联通图的集合幂级数,同时已知每个集合的权值为 \(h(x)\),我们能否求出联通子图所对应的权值和。
如果能解决这个问题,且解决过程可逆,那联通子图权值和是可做问题,进而可以解出 \(f(x)\)。
边双联通生成子图计数:
设 \(p_u(S)\) 表示割边两端点的编号均不超过 \(u\) 的联通子图权值和。
那么对于 \(p_0(S)=f_S \times h_S\),因为不能有割边。而 \(p_n(S)\) 则相当于联通子图权值和,因为割边不受限制。
需要解决从 \(p_{u-1}\) 推向 \(p_u\)。
新增的割边端点必定是 \(u\),考虑新增的部分,去掉所有 \(u\) 链接的割边后,一定分成若干联通块:\(P,T_1,T_2,T_3,……,T_k\),其中 \(u \in P,u \not \in T_i,P \cap T_i=\emptyset\),这些联通块贡献的形式是“无序组合”,尝试用 \(\operatorname{exp}\) 来描述。注意 \(P\) 是特别的,\(T\) 是类似的。\(T\) 需要和 \(P\) 连边,因此还需要刻画贡献。
设 \(q_{u-1}(S)=[u \not\in S]p_{u-1}(S) \times w(u,S)\),其中 \(w(u,S)\) 表示 \(S\) 中编号比 \(u\) 小的点有多少个,这些点与 \(u\) 相连将会成为链接联通块之间的割边。
则有递推式:
注意这里面的函数都有定义域,如果不在域内不需要考虑。比如 \(p_u(S)=p_{u-1}(S),u \not \in S\)。
我们需要考察这个做法是否可逆,也就是说,现在解决了如何从 \(p_0\) 推向 \(p_n\),接下来需要解决 \(p_n\) 推向 \(p_0\)。
转移方程为 \(p_{u}=p_{u-1} * \operatorname{exp}(q_{u-1})\),知道 \(p_{u-1}\),我们还需要知道 \(p_u\) 和 \(\operatorname{exp}(q_{u-1})\),而 \(q_{u-1}(S)=[u \not\in S]p_{u-1}(S) \times w(u,S)\) 与 \(S,u \in S\) 无关,可以改写为 \(q_{u-1}(S)=[u \not\in S]p_{u}(S) \times w(u,S)\)。
因此有:
只需要做一次 \(\operatorname{exp}\),再做一次子集卷积即可。注意定义域的问题,我们只取 \(p_{u},p_{u-1}\) 中 \(u \in S\) 的部分,\(q_{u-1}\) 中 \(u \not \in S\) 的部分做变换。
如果我们设 \(h_s=1\),则最终得到 \(p_0\) 就是答案。
点双联通分量生成子图计数:
沿用上面的思路,仍然设 \(p_u\)。
考虑增加一个点之后的增加的部分是什么。
去掉割点后,依然是剩下一些联通块,且仍然是以“不交无序组合”的形式贡献。
这次我们不需要再处理连边,而是直接使用包含割点的联通块信息,因此这些联通块实际形态应该是:全部包含 \(u\),去掉 \(u\) 之后的部分两两无交。
因此我们把所有包含 \(u\) 的 \(S\) 先去掉 \(u\),再做 \(\operatorname{exp}\),然后再把得到的部分加上 \(u\) 替换当前的值。
逆过程是相对箱单的:把所有 \(S,u \in S\) 先去掉 \(u\),然后求 \(\operatorname{ln}\) 分解后再替换当前的值即可。
一个常数优化是,每次我们不从新扣出来包含 \(u\) 的然后再加上 \(u\),这需要做两次 FWT,太慢了。
我们直接使用 \(FWT\) 上的差分算出来,把上面这部分的 FWT 值挂在 \(S,u \in S\) 上面,做 \(\operatorname{ln}\),然后再加回来就好了。
常数优化的参考实现
for(int u=1;u<(1<<n);u<<=1)
{
for(int p=0;p<(1<<n);p+=u*2)
{
for(int i=p;i<p+u;i++)
{
static int a[N+1];
for(int j=1;j<=n;j++) Add(f[j][i+u],mod-f[j][i]),a[j-1]=f[j][i+u];
Exp(a);
for(int j=1;j<=n;j++) f[j][i+u]=a[j-1],Add(f[j][i+u],f[j][i]);
}
}
}

浙公网安备 33010602011771号