集合幂级数
定义
集合幂级数就是长得像 的东西 ,其中这个二进制数 表示一个 的一个子集。
一些基本操作
很多题目要做的就是以下这几种操作:
-
高维前缀和: 。
-
高维后缀和: 。
-
或卷积: 。
-
与卷积: 。
-
异或卷积: 。
-
子集卷积: 。
-
子集卷积 exp : 。
-
多项式复合集合幂级数: ,其中 为子集卷积。
如果上述操作我们暴力求解,复杂度均为 。我们依次考虑这些问题如何在比较好的时间复杂度内解决。
-
高维前缀和
我们考虑怎么做一维前缀和:
1
for(int i=1;i<=n;i++) a[i]+=a[i-1];
二维前缀和呢:
1
2for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) a[i][j]+=a[i][j-1];
for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) a[i][j]+=a[i-1][j];我们以此类推, 维前缀和即为每次枚举一维,将这一维上做一次前缀和:
1
2
3for(int i=0;i<n;i++)
for(int j=0;j<(1<<n);j++)
if(j&(1<<i)) a[j]+=a[j^(1<<i)];这样我们就以 的时间复杂度解决了高维前缀和问题。
我们称这个高维前缀和过程为快速莫比乌斯变换,即FMT。
-
高维后缀和
我们效仿高维前缀和,每次枚举一维做后缀和即可。
1
2
3for(int i=0;i<n;i++)
for(int j=0;j<(1<<n);j++)
if(j&(1<<i)) a[j^(1<<i)]+=a[j]; -
或卷积
我们定义 经过 FMT 后得到的集合幂级数为 , 经过 FMT 后得到的集合幂级数为 , 经过 FMT 后得到的集合幂级数为 ,我们容易发现 。
所以我们只需要对 分别做一次 FMT ,并将对应位相乘后做一次 FMT 的逆变换即可。
而 FMT 的逆变换显然就是把刚才的过程反过来,即为:
1
2
3for(int i=0;i<n;i++)
for(int j=0;j<(1<<n);j++)
if(j&(1<<i)) a[j]-=a[j^(1<<i)];这样就求出了 的所有系数,在 的时间复杂度解决了或卷积。
-
与或卷积类似,我们对 分别做一次高维后缀和,并将对应位相乘后做一次高维后缀和的逆运算即可。
时间复杂度 。
-
异或卷积
我们定义一个算子 ,其中 和 都是一个集合幂级数。 。
我们想要说明如果 和 异或卷积后的结果为 ,那么有 。
证明:

(长行公式无法渲染。。。。。)
所以我们要做的事情就是分别对 求出 后对应相乘再做 的逆变换即可。和高维前缀和相似,我们对每一位依次考虑。对于第 位和一个不包含 的集合 ,设 ,则有新的 。这样我们就以 的时间复杂度求出了 。
1
2
3
4
5
6
7
8for(int i=1;i<(1<<n);i<<=1){
for(int j=0;j<(1<<n);j+=(i<<1)){
for(int k=j;k<j+i;k++){
int x=a[k],y=a[k+i];
a[k]=x+y; a[k+i]=x-y;
}
}
} 的逆运算直接对每一位做逆操作即可。
时间复杂度 。
-
子集卷积
考虑如果我们做普通的或卷积,那么会有一些 的 对贡献到 上,所以我们不能直接做或卷积。
但注意到 的条件等价于 ,所以我们可以将所有集合按元素个数分组,将第 组和第 组或卷积得到的集合幂级数中元素个数恰为 的部分贡献到最终答案中。
如果我们对所有 对 暴力做或卷积,复杂度会是 。但我们发现这样做对每一组操作了 次 ,这是多余的操作。所以可以事先算出所有组的 ,然后对所有 对 的 数组直接对应位相乘加到答案,最后再把答案的每个组用 的逆运算操作回去即可。
时间复杂度 。
-
子集卷积 exp
考虑按最高位分组,每一组中最多选择一个,所以答案即为所有组子集卷积后的答案。考虑从低位到高位依次合并,合并第 位时的复杂度为 ,总复杂度为 。
-
仍然按最高位分组,且每一组中最多选择一个,从低位到高位合并,但我们要记录当前还有几个要选。记 表示合并完前 组后还需要再添加 个的方案数。初始即为 ,每次添加一组即为 ,其中 表示第 组形成的集合。总复杂度为 。
时间复杂度 。
一些例题
-
WC2018 (luogu) 州区划分
-
P5933 带边权连通图个数
-
loj154 #154. 集合划分计数