集合幂级数学习笔记

集合幂级数学习笔记

网上的东西大多数比较杂,本文大致总结了一下,主要参考了 APIO2025 cxy 的课件。

前置知识:高维前缀和(SOSDP)

定义

什么是集合幂级数

一个形式化的定义为,设全集 \(U = \{1,2,\ldots,n\}\)\(U\) 上的集合幂级数 \(f\) 是一个 \(2^U\to R\) 的映射,其中 \(2^U\) 表示 \(U\) 的所有子集构成的集合,\(R\) 为一个交换环(可以简单理解为实数),在 OI 中一般为模某个大质数的整数域 \(\mathbb{Z}_p\)。可以简单理解为,将每个 \(S\subseteq U\) 带入 \(f\) 都能得到一个 \(R\) 中的数,也相当于将形式幂级数中的序列换成了集合。

运算

集合幂级数的运算一般都定义在同一个 \(U\) 下,有以下基本运算:

  • 加法,即对位相加,\((f+g)_S = f_S+g_S\)
  • 乘法,两个不交的集合可以贡献到并集,即 \((f\times g)_S = \sum\limits_{T\subseteq S} f_Tg_{S\setminus T}\)
  • 集合并乘法,任意两个集合可以贡献到并集,\((f*g)_S = \sum\limits_{L\cup R = S}f_Sg_T\)

由于 \(R\) 是交换环,所以同理可以定义减法。

加法直接做就是 \(\mathcal{O}(2^n)\) 的,而第二种乘法暴力做是 \(\mathcal{O}(3^n)\) 的,第三种是 \(\mathcal{O}(4^n)\) 的,下面反别介绍这两种乘法的优化方式。

快速沃尔什变换(FWT,FMT)

P4717 【模板】快速莫比乌斯 / 沃尔什变换 (FMT / FWT)

FWT 用于一类形如 \((\oplus,\times)\) 的卷积问题,即给定集合幂级数 \(a,b\),定义 \((a*b)_i = \sum\limits_{j\oplus k = i}a_ib_j\),一般 \(\oplus\)\(or,and,xor\) 中的一种。如果 \(\oplus\) 为加法就是多项式卷积,我们的做法是 DFT 和 IDFT,其中 DFT 能快速地将多项式转为点值地形式,于是就能对位相乘再 IDFT。

类似的,我们希望找到一种变换 fwt,使得 \(a\to fwt(a)\)\(\mathcal{O}(2^nn)\) 的,并且满足 \(fwt(a)_i\times fwt(b)_i = fwt(a*b)_i\)

Or 卷积

定义 \(fwt(a)_i = \sum\limits_{j\subseteq i} a_j\),于是:

\[fwt(a)_i\times fwt(b)_i = \sum_{j\subseteq i}a_j\sum_{k\subseteq i}b_k = \sum_{j\subseteq i,k\subseteq i}a_jb_k = \sum_{(j|k)\subseteq i}a_jb_k = fwt(a*b)_i \]

计算 \(fwt(a)\) 相当于做一遍高维前缀和,复杂度 \(\mathcal{O}(2^nn)\),当然也可以使用分治。逆运算为 \(ifwt\),在高维前缀和或分治时将加法变为减法即可。

还有另一种理解方法,即钦定 \(j|k\) 只有集合 \(S\) 中的位值为 \(1\),那么可以拆分为两个独立的条件 \(j\subseteq S,k\subseteq S\),于是分别对 \(a,b\) 做高维前缀和再对位相乘。但因为我们钦定了 \(S\) 中为 \(1\),实际上有些位可能不为 \(1\),所以要做一遍子集反演,即高维前缀和的逆运算。

And 卷积

定义 \(fwt(a)_i = \sum\limits_{i\subseteq j}a_j\),和上面同理。

Xor 卷积

定义 \(fwt(a)_i = \sum\limits_j (-1)^{|i\&j|}a_j\),手玩可以发现满足性质,同理可以分治求解。

本质是什么

不妨思考 FWT 的本质是什么,我们想找到一个函数 \(c(i,j)\) 表示 \(a_j\)\(fwt(a)_i\) 的贡献,即 \(fwt(a)_i = \sum\limits_j a_jc(i,j)\)。因为 \(a_ib_j\) 会贡献到 \((a*b)_{i\oplus j}\),所以 \(\forall i,j,k\),要满足 \(c(i,j)c(j,k) = c(i,j\oplus k)\),例如 Or 卷积就是 \([j\subseteq i][k\subseteq i] = [(j|k)\subseteq k]\)。可以发现 \(c(i,j)\) 对每位是独立的,也就是说我们只需要设定 \(\begin{bmatrix}c(0,0) & c(0,1) \\ c(1,0) & c(1,1)\end{bmatrix}\) 即可,而逆变换的矩阵就是上述矩阵的逆矩阵,所以要求上述矩阵可逆,即两行不能相同且不能有一行全 \(0\)

例如 Or 卷积,要求 \(c(x,0)c(x,0) = c(x,0),c(x,0)c(x,1) = c(x,1),c(x,1)c(x,1) = c(x,1)\),其余两种运算同理,\(c(x,0) = c(x,1) = 1\) 显然在所有情况中都合法。对于 Or 卷积,可以再设 \(c(x,0) = 1,c(x,1) = 0\);对于 And 卷积,设 \(c(x,0) = 1,c(x,1) = 0\);对于 Xor 卷积,设 \(c(x,0) = 1,c(x,1) = -1\)

对于 Or 卷积,原矩阵和逆矩阵分别为:

\[\begin{bmatrix}1 & 0 \\ 1 & 1\end{bmatrix},\begin{bmatrix}1 & 0 \\ -1 & 1\end{bmatrix} \]

对于 And 卷积,原矩阵和逆矩阵分别为:

\[\begin{bmatrix}1 & 1 \\ 0 & 1\end{bmatrix},\begin{bmatrix}1 & -1 \\ 0 & 1\end{bmatrix} \]

对于 Xor 卷积,原矩阵和逆矩阵分别为:

\[\begin{bmatrix}1 & 1 \\ 1 & -1\end{bmatrix},\begin{bmatrix}\frac 1 2 & \frac 1 2 \\ \frac 1 2 & -\frac 1 2\end{bmatrix} \]

矩阵的意义还能拓展到 \(K\) 维 FWT,具体参考快速沃尔什变换 - OI Wiki

记录详情 - 洛谷 | 计算机科学教育新生态

注意 \(fwt(a+b) = fwt(a)+fwt(b)\),后面会经常利用这个性质来优化复杂度。对于 \(k\) 个集合幂级数 \(f_1,\ldots,f_k\),有 \(fwt(f_1*\ldots *f_k) = \prod\limits_{i=1}^k fwt(f_i)\)

P13497 【MX-X14-T7】墓碑密码

给定两个不可重集 \(S,T\)\(q\) 次询问,每次给定 \(n\),求有多少个大小不超过 \(n\) 的可重集合 \(a\),满足 \(a\) 的元素都 \(\in S\),并且 \(a\) 中所有元素的异或和 \(\in T\)

\(|S|,|T|\le 128,S_i,T_i < 2^{28},n\le 10^8,q\le 10^5\)

发现我们只关心 \(S\) 每个元素出现次数的奇偶性,设有 \(res_k\) 表示有多少个大小为 \(k\) 的不可重集满足条件,则对答案的贡献为 \(res_k{\lfloor\frac{n-k}2\rfloor+|S|\choose |S|}\),所以结果是一个关于 \(n\)\(|S|\) 次多项式,注意奇偶的多项式要分别考虑,现在问题变成了求 \(res_k\)

写成生成函数,设 \(h = \sum res_i y^i\),则 \(h = \sum\limits_{u\in T}[x^u]\prod\limits_{v\in S}(1+x^vy)\),其中 \(x\) 是集合幂级数,做异或卷积,\(y\) 为形式幂级数,做加法卷积。使用 FWT,有:

\[\begin{aligned} h &= \sum_{u\in T}[x^u]\prod_{v\in S}(1+x^vy) \\ &= \sum_{u\in T}[x^u]ifwt(\prod_{v\in S}fwt(1+x^vy)) \\ &= \sum_{u\in T}\sum_{i=0} (-1)^{|u\&i|}\prod_{v\in S}(1+(-1)^{|v\& i|}y) \\ &= \sum_{i=0}(\sum_{u\in T}(-1)^{|u\&i|})\prod_{v\in S}(1+(-1)^{|v\& i|}y) \end{aligned} \]

可以发现后半部分每一项要么是 \((1+y)\) 要么是 \((1-y)\),所以只需要统计出 \(1-y\) 的出现次数,最后再一起处理 \((1-y)^k(1+y)^{|S|-k}\) 这个多项式的贡献即可,这部分不是瓶颈。现在问题就变为了对每个 \(i\)\(\sum\limits_{u\in T}(-1)^{|u\&i|}\),后半部分同理。将所有 \(T\) 内元素的二进制一行一行的写出来,那么就是选 \(i\) 中为 \(1\) 的那些列异或起来,统计最终的数列中 \(1\) 的个数。

于是可以将每一列写成一个 int128,然后异或起来求 popcount,复杂度 \(\mathcal{O}(\frac{2^{28}\times 28\times |S|}{w})\)。进一步地,按格雷码地顺序来枚举,每次就只有一位会改变,于是总复杂度为 \(\mathcal{O}(\frac{2^{28}|S|}{w}+|S|^3+q|S|)\)

记录详情 - 洛谷 | 计算机科学教育新生态

子集卷积

P6097 【模板】子集卷积

\((f\times g)_S = \sum\limits_{L\cup R = S,L\cap R = \empty}f_Lg_R\),子集卷积相较于 Or 卷积多了 \(L\cap R = \empty\) 的限制,注意到在 \(L\cup R = S\) 的情况下有 \(|L|+|R|\ge |S|\),也就是说 \(|L|+|R|=|S|\) 就等价于 \(L\cap R = \empty\)。于是设 \(f_S' = f_S x^{|S|},g_S' = g_S x^{|S|}\),那么 \((f\times g)_S = [x^{|S|}](f'*g')_S\)

相当于我们用一维表示集合,一维表示集合大小,第一维做集合并乘法,第二维做多项式乘法,所以只有当 \(L\cap R = \empty\)\(f_Lg_R\) 才能贡献到 \((f\times g)_{L\cup R}\)。设 \(F_i = \sum [x^i]f',G_i = \sum[x^i]g'\),于是 \(f\times g = \sum\limits_{i,j}F_i*G_j\),于是问题就变为了做 \(n^2\) 次及合并乘法,因为 \(fwt(f\times g)_S = \sum\limits_{i,j}fwt(F_i)_Sfwt(G_i)_S\),所以对所有 \(F_i,G_i\) 做一遍 FWT 后,再对所有 \(S\)\(\mathcal{O}(n^2)\) 的多项式乘法即可,两部分复杂度都是 \(\mathcal{O}(2^nn^2)\)

n = rd();
for(int i = 1;i < 1<<n;i++)pop[i] = pop[i&i-1]+1;
for(int i = 0;i < 1<<n;i++)f[pop[i]][i] = rd();
for(int i = 0;i < 1<<n;i++)g[pop[i]][i] = rd();
for(int i = 0;i <= n;i++)Or(f[i],1),Or(g[i],1);
for(int i = 0;i <= n;i++)for(int j = 0;i+j <= n;j++)
    for(int S = 0;S < 1<<n;S++)
        (h[i+j][S] += f[i][S]*g[j][S]) %= mod;
for(int i = 0;i <= n;i++)Or(h[i],-1);
for(int i = 0;i < 1<<n;i++)printf("%lld ",(h[pop[i]][i]+mod)%mod);

记录详情 - 洛谷 | 计算机科学教育新生态

P4221 [WC2018] 州区划分

容易写出 \(\mathcal{O}(3^n)\) 转移式子为 \(f_S = \sum\limits_{T\subseteq S,T\ne \empty}g_T f_{S\setminus T}(\frac{sum_T}{sum_S})^p\),与传统的子集卷积不同的是,每次做完后要给 \(f_S\) 乘上 \((\frac 1 {sum_S})^p\),于是在上述子集卷积过程中,按集合大小从小到大来枚举。每次做完同一个大小的集合后,要 IFWT 回来乘上 \((\frac 1 {sum_S})^p\) 并 FWT 回去,这种方法也称为半在线卷积,复杂度还是 \(\mathcal{O}(2^nn^2)\)

记录详情 - 洛谷 | 计算机科学教育新生态

集合幂级数复合

对于一个 \(f_\empty = 0\) 的集合幂级数 \(f\) 与多项式 \(h\),定义 \(h(f) = \sum\limits_{i=0}^\infty h_if^i\),但因为 \(i>n\)\(f\) 全为 \(0\),所以 \(h\) 只有 \(0\sim n\) 项有用。和子集卷积一样,根据 \(fwt(h(f))_S = \sum\limits_{i=0}^\infty h_i fwt(f)_S^i\),对 \(\sum\limits_S f_Sx^{|S|}\) 做 FWT 后,就变成了对每个 \(S\) 做有 \(n\) 次的多项式复合。暴力复合复杂度为 \(\mathcal{O}(n^3)\),用多项式科技可以做到 \(\mathcal{O}(n\log^2 n)\),所以可看作总复杂度是 \(\mathcal{O}(n\log^2 n)\),当然一般情况下 \(h\) 有性质,可以用简单的方法做到 \(\mathcal{O}(n^2)\) 复合。

集合幂级数 exp,ln,inv

P12230 【模板】集合幂级数 exp

相当于 \(h = \sum\limits_{i=0}^\infty \frac{x^i}{i!}\),考虑如何在 \(\mathcal{O}(n^2)\) 的时间内对一个给定多项式 \(F\) 求出 \(G = e^F\)。两边同时求导得 \(G' = F'G\),即:

\[(n+1)G_{n+1} = \sum_{i=0}^n (i+1)F_{i+1}G_{n-i} \\ nG_n = \sum_{i=0}^{n-1}(i+1)F_{i+1}G_{n-1-i} \\ G_n = \frac{\sum\limits_{i=1}^n iF_iG_{n-i}}{n} \]

于是可以 \(\mathcal{O}(n^2)\) 递推求出 \(G\)。根据上面的方法,求出 \(fwt(\sum\limits_S f_Sx^{|S|})\) 后,对每个 \(S\) 做多项式 exp 即可,复杂度 \(\mathcal{O}(2^nn^2)\)。集合幂级数 ln,inv 可以类似求出,递推方法可以参考 各种多项式操作的 n^2 递推 - tzc_wk - 博客园

记录详情 - 洛谷 | 计算机科学教育新生态

和形式幂级数一样,集合幂级数 exp 也是有组合意义的,根据 \(e^x = \sum\limits_{i=0}^\infty \frac{x^i}{i!}\) 可以看出,\((e^f)_S\) 相当于将 \(S\) 划分为若干个集合 \(T_1,\ldots,T_k\),贡献为 \(\prod\limits_{i=1}^k f_{T_i}\),注意这些集合间是无序的。设 \(g = e^f\),写成 dp 形式就是 \(g_S = \sum\limits_{u\in T\subseteq S}f_T g_{S\setminus T}\)。而 ln 就是 exp 的逆运算。

逐点牛顿迭代法

名字看起来比较高级,但通俗来讲就是一位一位的做集合幂级数操作。一个例子就是非质数模数的集合幂级数 exp,传统方法涉及到逆元不太好做,考虑用组合意义来做。设 \(g = e^f\),依次考虑元素 \(0,\ldots,n-1\),在考虑第 \(i\) 个元素时我们已经知道了 \(g_{S\subseteq \{0,1,\ldots,i-1\}}\),现在要求 \(g_{i\in S\subseteq\{0,1,\ldots,i\}}\),转移同上。

因为所有需要求的 \(g\) 之间不会互相转移,所以可以直接用子集卷积,即将 \(g_{S\subseteq \{0,1,\ldots,i-1\}}\)\(f_{i\in S\subseteq\{0,1,\ldots,i\}}\) 乘起来即可。复杂度为 \(\sum\limits_{i=1}^n \mathcal{O}(2^ii^2) = \mathcal{O}(2^nn^2)\)

在上述算法中,如果问题变为求出 \(f_{S,k}\) 表示将 \(S\) 划分为 \(k\) 个无序集合的权值之和,看起来要多记录一维复杂度是 \(\mathcal{O}(2^nn^3)\),但实际上我们将上述过程倒过来并记录已经选了多少个集合,复杂度为 \(\sum\limits_{i=1}^n (n-i)2^ii^2 = \mathcal{O}(2^nn^2)\)

例题:P13843 【模板】集合幂级数 exp(非素数模数)

连通性问题

熟练运用 exp 的组合意义。

数连通子图

给定一个无向图 G,求 \(G\) 有多少边子图是连通的。

可以容斥枚举包含编号最小点的集合,然后 \(\mathcal{O}(3^n)\),容易用子集卷积优化到 \(\mathcal{O}(2^nn^2)\)

\(f_S\) 表示 \(S\) 导出子图中有多少边子图是连通的,那么设 \(g = e^f\)\(g\) 就相当于任意选一些连通图拼起来,也就是说 \(g_S\) 恰好考虑了 \(S\) 的所有边子图,即 \(g_S = 2^{edge(S,S)}\)。用 \(f = \ln g\) 反推出 \(f\),复杂度 \(\mathcal{O}(2^nn^2)\)

练习:P11734 [集训队互测 2015] 胡策的统计

数连通二分子图

[ARC105F] Lights Out on Connected Graph

给定一个无向图 G,求 \(G\) 有多少边子图是连通的二分图。

一个粗略的想法是枚举左部点集合 \(S\),贡献为 \(2^{edge(S,U\setminus S)}\),但这样一个有 \(c\) 个连通块的会被贡献 \(2^c\) 次。于是设 \(g_S\) 表示 \(S\) 所有边子图的 \(2^c\) 之和,\(f_S\) 表示 \(S\) 所有连通二分图边子图数量乘二,于是有 \(g = e^f\)。考虑求 \(g\)\(g_S = \sum\limits_{T\subseteq S} 2^{edge(T,S\setminus T)} = \sum\limits_{T\subseteq S} 2^{sum_S}2^{-sum_T}2^{-sum_{S\setminus T}}\)\(sum_S\) 表示 \(S\) 内的边数,于是一次子集卷积求出 \(g\),再 \(f = \ln g\) 可以得出 \(f\),答案为 \(\frac {f_U} 2\)。复杂度 \(\mathcal{O}(2^nn^2)\)

无根树计数

给定一个无向图 G,对每个子集 \(S\) 求出 \(S\) 导出子图中生成树数量。

直接用矩阵树定理复杂度是 \(\mathcal{O}(2^nn^3)\)

考虑逐点添加,对于一个 \(i\in S\subseteq \{0,1,\ldots,i\}\),考虑求 \(f_S\),可以发现去掉点 \(i\) 后会形成若干个连通块,每个连通块的贡献就是 \(i\) 向这个连通块的边数。于是设 \(g_S = edge(i,S)f_S\),求一遍 \(e^g\) 即可。复杂度为 \(\mathcal{O}(2^nn^2)\)

#6787. 色多项式

给定一张无向图 G,求出 G 的色多项式 \(P(x)\),满足对于任意正整数 \(k\),图 G 的 \(k\) 染色方案数为 \(P(k)\)。答案对 \(998244353\) 取模。

\(n\le 21\)

因为最多有 \(n\) 种本质不同的颜色,所以 \(P(x)\)\(n\) 次多项式。可以发现只需求出 \(res_k\) 表示将 G 划分为 \(k\) 个独立集的方案数,就能求出 \(k\) 染色方案数,然后差值求出 \(P(x)\)

\(f_{S,k}\) 表示将点集 \(S\) 划分为 \(k\) 个独立集的方案,设 \(g = \sum\limits_S[S为独立集]x^S\),则 \(f_{S,k} = [x^S]g^k\),用逐点牛顿迭代法可得复杂度为 \(\mathcal{O}(2^nn^2)\)

AT_xmascon22_f Fast as Fast as Ryser

给定一张无向图 G,对每个 \(0\le k\le \lfloor \frac n 2\rfloor\) 求出 G 中大小为 \(k\) 的匹配数。

答案对 \(2^{64}\) 取模。\(n\le 40\)

弱化版:Fast as Ryser - 题目 - QOJ.ac

添加 \(\lfloor \frac n 2\rfloor\)\((2i-1,2i)\) 的虚边,于是任何一个匹配加上这些虚边就是一些虚实交替的环和链,其中链首尾都是虚边,设有 \(x\) 条链,则匹配数为 \(\lfloor \frac n 2\rfloor-x\)。考虑求出 \(f_S,g_S\) 分别表示恰好包含集合 \(S\) 内的虚边的环数、链数,于是有 \(x\) 条链的答案就是 \(\exp(f)\times g^k\)

考虑求解 \(f_S,g_S\),设 \(dp_{S,i}\) 表示起点为 \(lowbit(S)\),终点为 \(i\) 的路径数,如果存在一条走回起点的实边则可以贡献。链如果直接 dp 每条链算 \(2\) 次,可以像环一样钦定起点为 \(lowbit(S)\),然后先 dp 左侧的虚实链再 dp 右侧,复杂度都是 \(\mathcal{O}(2^nn^2)\)

最后算答案先算出 \(\exp(f)\),然后用和上题一样的套路处理 \(g^k\) 即可,复杂度 \(\mathcal{O}(2^nn^2)\)

练习:P14270 ABC253Ex 加强版

QOJ5411 杏仁

给定 \(s,t\),称一张有向图 G 为杏仁,当且仅当:

  • \(s\) 出发能到达所有点,\(s\) 入度为 \(0\)\(t\) 出度为 \(0\),其余点的入度、出度都为 \(1\)

给定一张有向图 G 和 \(s,t\)\(q\) 次询问,每次给定一个 \(u\),求 G 有多少张杏仁子图包含边 \(s\to u\)

答案对 \(998244353\) 取模。\(n\le 22\)

考虑没有包含 \(s\to u\) 的限制怎么做。可以发现一张有向图是杏仁当且仅当能被划分为若干条 \(s\to t\) 的路径,满足这些路径只在 \(s,t\) 相交。设 \(f_S\) 表示路径点集为 \(S\) 的方案数,可以通过设 \(g_{S,i}\) 表示有多少条路径点集为 \(S\) 且最后一个点为 \(i\) 来 dp 求出,那么 \(h = \exp f\) 就是答案。

如果需要包含边 \(s\to u\),求出这条路径的点集 \(S\),对 \(h\) 做高维前缀和,那么贡献为 \(h'_{U\setminus S}\)。但如果对每个 \(u\) 都 dp 复杂度为 \(\mathcal{O}(2^nn^3)\),所以需要从 \(t\) 开始倒着 dp,复杂度 \(\mathcal{O}(2^nn^2)\)

#155. Tutte 多项式

一个非常牛逼的东西,具体有哪些用处可以见原题。

给定一张无向图 \(G = (V,E)\),定义 \(T_G(x,y) = \sum\limits_{A\subseteq E}(x-1)^{k(A)-k(E)}(y-1)^{k(A)+|A|-|V|}\),其中 \(k(A)\) 表示 \((V,A)\) 的连通块数。

给定 \(x,y\),求 \(T_G(x,y)\) 的值,答案对 \(998244353\) 取模。\(n\le 21\)

可以发现每个连通块是独立的,结果为每个连通块答案的乘积,现在只考虑原图连通的情况。注意不能直接拆贡献,因为有 \(0^0=1\) 的情况。对于一个 \(A\) 中的连通块点集 \(S\),设 \(f_S\) 表示其对 \(y\) 项的贡献,即 \(f_S = \sum\limits_{A}(y-1)^{|A|-(|S|-1)}\),其中 \(A\)\(S\) 导出子图中的一个连通子图的边集,贡献为 \(y-1\) 的非树边个数次方。

知道 \(f\) 后,答案为 \(g = \sum\limits_{i\ge 1}\frac{f^i}{i!}(x-1)^{i-1}\),特判 \(x=1\),将 \((x-1)\) 放进 \(f_S\) 后有 \(g = \frac{\exp f'}{x-1}\)。最后考虑如何求 \(f\),考虑逐点迭代并用类似生成树的方法,那么在考虑点 \(u\) 时设点集 \(S\)\(u\) 中有 \(tot\) 条连边,则贡献为 \(\sum\limits_{i=1}^{tot}{tot\choose i}(y-1)^{i-1} = \frac{y^{tot}-1}{y-1}\),依然要特判 \(y=1\),总复杂度为 \(\mathcal{O}(2^nn^2)\)

有向图上的问题

有向图上常见的限制包括强连通、连通、无环(DAG),或者一些复合等。

有向图计数转移里面一般会涉及到 \(edge(S,S\setminus T)\),这个式子不太好化简,所以一般只能做到 \(\mathcal{O}(3^npoly(n))\)

DAG 定向

给定一张无向图 G,求有多少种定向方式使得得到的有向图无环。

\(n\le 20\)

考虑如何刻画一个 DAG,DAG 有很明显的递归到子结构的方式,每次删去一个入度为 \(0\) 的点即可,但这样每次递归到子结构的方式不唯一,于是可以考虑同时删去所有入度为 \(0\) 的点。设 \(f_S\) 表示 \(S\) 导出子图的定向方案,每次枚举 \(T\subseteq S\) 表示 \(T\) 为所有入度为 \(0\) 的点,要求 \(T\) 之间没有连边,然后递归到子问题 \(f_{S\setminus T}\)

但这样有个问题,我们没有保证 \(S\setminus T\) 的所有点入度都不为 \(0\),而如果再记录一维表示哪些点入度不为 \(0\) 复杂度就太高了。可以发现对于某种最终的定向方案,设有 \(k\) 个入度为 \(0\) 的点,那么一共会被算 \(2^k-1\) 次,我们需要给每次计算都赋上一个权值,使得每种定向方案都恰好被计算了一次。

如果 \(k=1\),则要求 \(|T|=1\) 时权值为 \(1\)\(k=2\)\(|T| = 1\) 会计算两次,所以 \(|T|=2\) 时权值为 \(-1\)。一直推下去可以发现贡献为 \((-1)^{|T|+1}\),这是因为 \(\sum\limits_{i=1}^k{k\choose i}(-1)^{i+1} = 1\),所以转移式子可以写为 \(f_S = \sum\limits_{T\subseteq S,T\ne \empty}[T内部无边](-1)^{|T|+1}f_{S\setminus T}\),用子集卷积优化即可,复杂度 \(\mathcal{O}(2^nn^2)\)

练习:P6846 [CEOI 2019] Amusement Park[ABC306Ex] Balance Scale

P11714 [清华集训 2014] 主旋律

给定一张有向图,求有多少子图是强连通的。

答案对 \(10^9+7\) 取模。\(n\le 15\)

考虑容斥,用总方案数减去图不强连通的方案数,注意到任何一张图缩点后就是 DAG,考虑对这张 DAG 计数。

\(f_S\) 表示 \(S\) 导出子图的强连通子图数,我们枚举所有入度为 \(0\) 的 SCC 的点集并为 \(T\),设 \(g_S\) 表示将 \(S\) 拆为若干个 SCC \(T_1,\ldots,T_k\),贡献为 \((-1)^{k+1}\prod\limits_{i=1}^kf_{T_i}\)。于是 \(g\) 的转移就是 \(g_S = \sum\limits_{u\in T\subseteq S}-f_Tg_{S\setminus T}\)。求解时 \(f_S\) 枚举 \(T\) 后,剩下的点之间就可以任意连边了,所以 \(f\) 的转移为 \(f_S = 2^{edge(S,S)}-\sum\limits_{T\subseteq S,T\ne \empty}g_T 2^{edge(S,S\setminus T)}\)

注意式子里 \(f_S,g_S\) 之间会相互转移,但因为只有一个强连通分量时 \(f_S\) 不应该容斥,所以先设 \(f_S=0\) 求出 \(g_S\),然后再求解 \(f_S\),最后将 \(g_S\) 加上 \(f_S\) 即可。现在需要快速求解 \(edge(S,S\setminus T)\),记录两个 bitset 表示 \(S\) 的所有入边和出边分别是哪些边,求解时将两个 bitset 与起来求个 count 即可,复杂度 \(\mathcal{O}(2^nm+3^n\frac m w)\)。另一种方法是先枚举 \(T\) 再从小到大枚举 \(S\setminus T\),这样 \(T\)\(S\setminus T\) 的连边就可以预处理出来了,复杂度为 \(\mathcal{O}(2^nm+3^n)\)

记录详情 - 洛谷 | 计算机科学教育新生态

P10221 [省选联考 2024] 重塑时光

\(res_i\) 表示将 \(U\) 划分为 \(i\) 个无序集合使得存在重排方案的方案数,显然可以通过 \(res_i\) 来计算最终答案,问题变为求出 \(res_i\)。考虑判断一种 \(U\) 的划分方案 \(T_1,\ldots,T_k\) 是否合法,首先每个 \(T_i\) 内部的顺序要合法,然后对于 \(T_i,T_j\),如果存在 \(u\in T_i,v\in T_j\) 且存在边 \(u\to v\) 那就连边 \(i\to j\),则要求连出来的图是个 DAG,构造方案就是按拓扑序放每个 \(T_i\) 即可。

首先可以 dp 求出 \(h_S\) 表示 \(S\) 内部有多少种顺序,套路地,设 \(f_{S,i}\) 表示将 \(S\) 划分为 \(i\) 个无序集合且这些集合连出来是个 DAG 的方案数,枚举 \(T\) 表示入度为 \(0\) 的集合的并集为 \(T\),设 \(g_{S,i}\) 表示将 \(S\) 划分为 \(i\) 个集合且集合间没有连边,所有集合的 \(h_T\) 之积的和。\(f,g\) 的转移可以类似上面写出,其中 \(f\) 的转移要做一个背包,复杂度为 \(\mathcal{O}(3^nn^2)\),可以用拉差优化背包做到 \(\mathcal{O}(3^nn)\)

记录详情 - 洛谷 | 计算机科学教育新生态

双连通性问题

#6729. 点双连通生成子图计数(点双连通-连通 变化)

题意如命。\(n\le 18\)

\(f_{i,S}\) 表示只存在编号 \(\le i\) 的割点,\(S\) 的连通子图数,显然 \(f_{0,U}\) 就是答案。考虑如何用 \(f_{i-1}\) 推到 \(f_i\)\(f_i\) 相比于 \(f_{i-1}\) 唯一的区别就是 \(i\) 可以成为割点。设去掉 \(i\)\(S\) 变为了若干个连通块 \(T_1,\ldots,T_k\),那么一个点集 \(T_j\) 的贡献就是 \(T_j\cup i\) 中只存在编号 \(\le i-1\) 的割点的连通子图数,即 \(f_{i-1,T_j\cup i}\)。也就是说如果知道了 \(f_{i-1}\),那么将所有 \(i\in S\)\(f_{i-1,S}\) 去掉 \(i\) 后做一遍 \(\exp\),然后再把 \(i\) 加回去即可,注意如果 \(i\not\in S\) 则有 \(f_{i,S} = f_{i-1,S}\)

反过来,如果知道了 \(f_i\),将 \(i\in S\)\(f_{i,S}\) 去掉 \(i\) 后做一遍 \(\ln\),再把 \(i\) 加回去即可,\(i\not\in S\) 同理。可以发现 \(f_{n,S}\) 就是 \(S\) 的连通子图数,一遍 \(\ln\) 即可求出,然后倒着推到 \(f_0\),复杂度为 \(\mathcal{O}(2^nn^3)\)

#6730. 边双连通生成子图计数(边双连通-连通 变化)

题意如名。\(n\le 18\)

\(f_{i,S}\) 表示只存在两个端点都 \(\le i\) 的割边,\(S\) 的连通子图数,其他部分都和上面一样,考虑如何从 \(f_{i-1}\) 推到 \(f_i\)

去掉所有与 \(i\) 相连且另一个点编号 \(<i\) 的边,设 \(S\) 变为了若干个连通块 \(T_0,T_1,\ldots,T_k\),其中 \(i\in T_0\),每个 \(T_j\) 的贡献为 \(f_{i-1,T_j}\),于是设 \(q_{i-1,S} = [i\not\in S]edge(i,S)f_{i-1,S}\),其中 \(edge(i,S)\) 表示 \(S\) 中有多少点与 \(i\) 相连且编号 \(<i\)。有 \(f_i = f_{i-1}\times \exp q_{i-1}\),同理如果 \(i\notin S\)\(f_{i,S} = f_{i-1,S}\)。那么从 \(f_i\) 倒推到 \(f_{i-1}\) 时,因为 \(i\not\in S\)\(f_{i-1,S}\) 都知道了,所以可以确定 \(q_{i-1}\),然后有 \(f_{i-1} = f_i\times (\exp q_{i-1})^{-1} = f_i\times \exp(-q_{i-1})\)。复杂度依然为 \(\mathcal{O}(2^nn^3)\)

#6719. 「300iq Contest 2」数仙人掌 加强版(生成仙人掌计数)

题意如名。\(n\le 18\)

\(f_{i,S}\) 表示只存在编号 \(\le i\) 的割点,\(S\) 的仙人掌子图数。\(f_{0,S}\) 只能有环,可以 dp 求出,\(f_{n,S}\) 记为所求。设去掉 \(i\)\(S\) 变为了若干个连通块 \(T_1,\ldots,T_k\),每个 \(T_j\) 的贡献为 \(f_{i-1,T_j\cup i}\),于是计算方法就和求点双子图一模一样了。

通过容斥求解边双子图数

定义 \(G = trans(F,c)\) 表示 \(G_S = \sum\limits_{T_1\cup\ldots\cup T_k = S}val(T_1,\ldots,T_k)c^{k-1}\prod\limits_{i=1}^k F_{T_i}\),其中 \(val(T_1,\ldots,T_k)\) 表示将 \(T_1,\ldots,T_k\) 连成一棵树的方案数。即 \(G_S\) 相当于将 \(S\) 划分为若干个不交的无序集合 \(T_1,\ldots,T_k\),每连一条边贡献为 \(c\),将这些集合连成一棵树的贡献之和。

考虑如何求 \(trans(F,c)\),设 \(f_{i,S}\) 表示只选择了两个端点都 \(\le i\) 的边的答案。和求边双一样,去掉所有与 \(i\) 相连且另一个点编号 \(<i\) 的边,设 \(S\) 变为了若干个连通块 \(T_0,\ldots,T_k\),其中 \(i\in T_0\),那么每个 \(T_j\) 的贡献为 \(f_{i-1,T_j}\)。设 \(q_{i-1,S} = [i\not\in S]edge(i,S)f_{i-1,S}\times c\),则 \(f_i = f_{i-1}\times \exp(q_{i-1})\),所以求解一次 \(trans(F,c)\) 的复杂度为 \(\mathcal{O}(2^nn^2)\)

根据定义,如果 \(f_S = [|S| = 1]\),那么 \(g = trans(f,1)\) 就表示生成树个数。如果 \(f_S\) 表示 \(S\) 导出子图的边双子图个数,那么设 \(g = trans(f,1)\),则 \(g_S\) 表示 \(S\) 导出子图的连通子图个数,相当于枚举 \(S\) 缩点后的每个强连通分量。反过来,如果想求 \(f_S\),我们可以钦定一些割边,每钦定一条割边贡献为 \(-1\),设所有割边将 \(S\) 划分为了集合 \(T_1,\ldots,T_k\),那么每个集合内部连通即可,即贡献为 \(\prod\limits_{i=1}^k g_{T_i}\),也就是说 \(f = trans(g,-1)\)。那么求出 \(g\) 后求一遍 \(trans(g,-1)\) 也可以 \(\mathcal{O}(2^nn^3)\) 求出 \(f\)

从上面的分析可以看出 \(trans(trans(f,-1),1) = f\),另一种理解方法为初始时有若干个集合,第一阶段可以在两个集合间连边,贡献为 \(-1\),第二阶段连边贡献为 \(1\)。那么如果连了至少一次边,这条边可能在第一阶段连也可能在第二阶段连,贡献为 \(-1+1=0\),所以只有一次边不连才会被计入,即 \(trans(trans(f,-1),1) = f\)

【UR #30】交通管制

给定一张无向图 G 和 \(k\) 条限制,每条限制形如 \((s_i,t_i,c_i)\),表示从 \(s_i\)\(t_i\) 至少有 \(c_i\) 条不同的边简单路径,其中 \(c_i\in \{1,2\}\),求有多少子图满足所有限制条件。

答案对 \(998244353\) 取模。\(n\le 16\)

\(c_i = 1\) 即要求 \(s_i,t_i\) 连通,\(c_i=2\) 即要求缩点后 \(s_i,t_i\) 的路径上至少有一个 \(siz\ge 2\) 的强连通分量。先不考虑 \(c_i=1\) 的限制,考虑求出 \(f_S\) 表示 \(S\) 有多少连通子图满足所有 \(S\) 内部的 \(c_i=2\) 的限制。将图缩点,设大小为 \(1\) 的强连通分量为白点,大小 \(\ge 2\) 的为黑点,那么这个子图合法当且仅当没有一对 \(s_i,t_i\) 在同一个白色极大连通块内。

\(g_S\) 表示 \(S\) 子图是一个极大连通块的方案数,即 \(S\) 的生成树个数,\(h_S\) 表示 \(S\) 子图是一个黑点的方案数,即 \(S\) 的边双连通子图数且要求 \(|S|\ge 2\)。如果有一对 \(s_i,t_i\) 同时在 \(S\) 内则将 \(g_S\) 设为 \(0\),答案就是将 \(g,h\) 连成一棵树的方案数。但要注意因为 \(g_S\) 已经是极大白色连通块了,所以两个 \(g\) 之间不能连边,可以考虑容斥,即钦定一些 \(g\) 之间的连边,贡献为 \(-1\)

把连边看为两个阶段,第一阶段只能在 \(g\) 之间连边,每条边贡献为 \(-1\),第二阶段可以在 \(g,h\) 之间连边,每条边贡献为 \(1\)。那么如果某张图在两个 \(g\) 之间连了边,可能是在第一阶段或是第二阶段,贡献为 \(-1+1=0\)。所以有 \(f = transf(transf(g,-1)+h,1)\)

最后考虑 \(c_i=1\) 的限制,那么一个连通块 \(f_S\) 合法当且仅当不存在一对 \((s_i,t_i)\) 满足恰好一个出现在 \(S\) 中(\(c_i=2\) 的也要考虑),所以会将一些 \(f_S\) 设为 \(0\),最后求一遍 \(\exp\) 即可,复杂度 \(\mathcal{O}(2^nn^3)\)

P11567 建造军营 II

给定一张无向连通图 G 和 \(k\)\((s_i,t_i)\),定义一张图的权值为,有多少种给每条边染上黑白的方案,使得对于每个 \((s_i,t_i)\) 都满足 \(s_i\)\(t_i\) 的所有边简单路径上的所有边都是黑色。求 G 的所有连通子图的权值之和。

答案对 \(10^9+7\) 取模。\(n\le 16\)

考虑如何求一张图的贡献,将图缩点,对于将每个 \((s_i,t_i)\) 路径上的强连通分量和割边都染黑,最后一个每条白边的贡献为 \(2\)(可能是白色割边,或是白强连通分量里的每条边)。将黑色割边与黑点形成的连通块称为极大黑色连通块,相当于用白边将一些黑色极大连通块和白点连城一棵树(注意与上题不同的是,两个黑色极大连通块间也能用白边)。

\(f_S\) 表示 \(S\) 的黑色极大连通块子图数,\(g_S\) 表示 \(S\) 的子图是白点的方案数,答案为 \(trans(f+g,2)\)。考虑求 \(f\),容斥钦定若干条白边,这些白边将原图划分为了若干个连通块,那么每个连通块要求对于每个 \((s_i,t_i)\) 都满足 \(s_i,t_i\) 都在连通块内或都在外,设 \(f'_S\)\(S\) 的连通子图数,将不满足条件的 \(f_S'\) 设为 \(0\) 后,有 \(f = trans(f',-1)\)

考虑求 \(g\),相当于求所有边双连通子图的权值之和,其中一张图的权值为 \(2^{edge}\)。钦定一些割边,设 \(g'_S\) 表示 \(S\) 所有连通子图的权值之和,则 \(g = trans(g',-2)\),而求解 \(g'\) 相当于设 \(g'_S = 3^{cnt_S}\) 然后求一遍 \(\ln\)。复杂度为 \(\mathcal{O}(2^nn^3)\),有点卡常。

记录详情 - 洛谷 | 计算机科学教育新生态

其他题目

P10104 [GDKOI2023 提高组] 异或图

给定一张无向图和一个长为 \(n\) 的数组 \(a\) 以及整数 \(C\),求有多少个长为 \(b\) 的数组满足:

  • \(\forall 1\le i\le n\),有 \(0\le b_i\le a_i\)
  • 对于每条边 \((u,v)\)\(b_u\ne b_v\)
  • \(b_1\oplus \ldots\oplus b_n = C\)

答案对 \(998244353\) 取模。\(n\le 15,a_i,C\le 10^{18}\)

考虑如果没有第二条限制怎么做,枚举 \(d\) 表示最大的位数使得存在 \(i\) 满足 \(a_i,b_i\) 二进制下第 \(d\) 位不同,也就是说这个 \(a_i\)\(0\sim d-1\) 位都可以自由填,那么只要更高位都满足条件,\(0\sim d-1\) 位其他数也可以任意填,最后 \(a_i\) 就恰好有一种方案,复杂度 \(\mathcal{O}(n\log C)\)

如果有第二条限制则考虑容斥,钦定一些边满足 \(b_u=b_v\),则原图会被划分为若干个连通块,对每个奇数大小的连通块取 \(b_i\) 最小的就变成了上面的问题,设 \(g_S\) 表示 \(S\) 每个连通子图的 \((-1)^{|E'|}\) 之和,可以通过枚举包含 \(lowbit(S)\) 的连通块来容斥求解,暴力枚举划分方案可以做到 \(\mathcal{O}(Bell(n)n\log V)\)

考虑优化,将 \(a_i\) 排序,我们只需要求出 \(f_S\) 表示有多少个最终只取 \(S\) 内的 \(a_i\) 的子问题的划分方案。从小到大枚举 \(i\),此时 \(f_S\) 的含义为,对于 \(<i\)\(j\)\(S\) 的第 \(j\) 位表示是否有一个划分出来的集合大小为奇数且最小元素为 \(j\),对于 \(\ge i\)\(j\)\(S\) 的第 \(j\) 是否被划分出去了。那么如果 \(i\not\in S\) 就不管,否则就要枚举一个 \(i\) 为最小元素的集合 \(T\) 划分出去,这样复杂度就是 \(\mathcal{O}(3^nn+2^nn\log )\)

总结

集合幂级数在近几年出现次数比较多,值得重视一下。做集合幂级数一个重要的思想就是递归到子结构,然后就是熟练运用连通与非连通的关系(如 \(\exp\)),如果方案会计重就给每种方案赋一个权值使得总和恰好是我们想要的。对于双连通问题,在缩点后的树上考虑,一般是计数极大连通块,然后再用 \(transf\)

一些链接(参考博客)

题目列表 - 洛谷

集合幂级数相关 - qAlex_Weiq - 博客园

连通 - 点双 / 边双连通变换学习笔记 | 菜王的 blog

集合幂级数在子图计数问题上的应用

集合幂级数入土 - 洛谷专栏

posted @ 2026-03-02 12:09  max0810  阅读(3)  评论(1)    收藏  举报