0%

day22-part2-集合幂级数-8-07

集合幂级数

定义

集合幂级数就是长得像 i=02n1aixi\sum\limits_{i=0}^{2^n-1}a_ix^i​ 的东西 ,其中这个二进制数 ii​ 表示一个 {1,2,,n}\{1,2,\cdots,n\}​ 的一个子集。

一些基本操作

很多题目要做的就是以下这几种操作:

  1. 高维前缀和: ci=j[ji=i]ajc_i=\sum\limits_j [j\vee i =i] a_j

  2. 高维后缀和: ci=j[ji=i]ajc_i=\sum\limits_j[j\wedge i=i] a_j

  3. 或卷积: ci=jk[jk=i]ajbkc_i=\sum\limits_j\sum\limits_k [j \vee k=i] a_jb_k​ 。

  4. 与卷积: ci=jk[jk=i]ajbkc_i=\sum\limits_j\sum\limits_k[j\wedge k=i]a_jb_k

  5. 异或卷积: ci=jk[jk=i]ajbkc_i=\sum\limits_j\sum\limits_k [j\oplus k=i] a_jb_k​ 。

  6. 子集卷积: ci=jk[jk=,jk=i]ajbkc_i=\sum\limits_j\sum\limits_k[j\wedge k=\emptyset,j\vee k=i] a_jb_k​ 。

  7. 子集卷积 exp : ci=i1,i2,,ik[i1+i2++ik=i,i1i2ik=i]ai1ai2aikc_i=\sum\limits_{i_1,i_2,\ldots,i_k} [|i_1|+|i_2|+\cdots+|i_k|=|i|,i_1\vee i_2\vee \cdots \vee i_k=i]a_{i_1}a_{i_2}\cdots a_{i_k}​ 。​

  8. 多项式复合集合幂级数: c=i=0nfiaic=\sum\limits_{i=0}^n f_ia^i ,其中 aia^i 为子集卷积。

如果上述操作我们暴力求解,复杂度均为 O(22n)O(2^{2n}) 。我们依次考虑这些问题如何在比较好的时间复杂度内解决。

  1. 高维前缀和

    我们考虑怎么做一维前缀和:

    1
    for(int i=1;i<=n;i++) a[i]+=a[i-1];

    二维前缀和呢:

    1
    2
    for(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];

    我们以此类推, nn​ 维前缀和即为每次枚举一维,将这一维上做一次前缀和:

    1
    2
    3
    for(int i=0;i<n;i++)
    for(int j=0;j<(1<<n);j++)
    if(j&(1<<i)) a[j]+=a[j^(1<<i)];

    这样我们就以 O(2nn)O(2^nn) 的时间复杂度解决了高维前缀和问题。

    我们称这个高维前缀和过程为快速莫比乌斯变换,即FMT

  2. 高维后缀和

    我们效仿高维前缀和,每次枚举一维做后缀和即可。

    1
    2
    3
    for(int i=0;i<n;i++)
    for(int j=0;j<(1<<n);j++)
    if(j&(1<<i)) a[j^(1<<i)]+=a[j];
  3. 或卷积

    我们定义 aa 经过 FMT 后得到的集合幂级数为 AAbb 经过 FMT 后得到的集合幂级数为 BBcc 经过 FMT 后得到的集合幂级数为 CC ,我们容易发现 Ci=AiBiC_i=A_iB_i

    所以我们只需要对 a,ba,b 分别做一次 FMT ,并将对应位相乘后做一次 FMT 的逆变换即可。

    而 FMT 的逆变换显然就是把刚才的过程反过来,即为:

    1
    2
    3
    for(int i=0;i<n;i++)
    for(int j=0;j<(1<<n);j++)
    if(j&(1<<i)) a[j]-=a[j^(1<<i)];

    这样就求出了 cc 的所有系数,在 O(2nn)O(2^nn) 的时间复杂度解决了或卷积。

  4. 与或卷积类似,我们对 a,ba,b​ 分别做一次高维后缀和,并将对应位相乘后做一次高维后缀和的逆运算即可。

    时间复杂度 O(2nn)O(2^nn)

  5. 异或卷积

    我们定义一个算子 FWT(a)FWT(a) ,其中 aaFWT(a)FWT(a)​ 都是一个集合幂级数。 FWT(a)i=j=02n1(1)ijajFWT(a)_i=\sum\limits_{j=0}^{2^n-1}(-1)^{|i\wedge j|} a_j

    我们想要说明如果 aabb 异或卷积后的结果为 cc ,那么有 FWT(c)i=FWT(a)iFWT(b)iFWT(c)_i=FWT(a)_i\cdot FWT(b)_i

    证明:


    (长行公式无法渲染。。。。。)
    所以我们要做的事情就是分别对 a,ba,b 求出 FWTFWT 后对应相乘再做 FWTFWT 的逆变换即可。

    和高维前缀和相似,我们对每一位依次考虑。对于第 ii​ 位和一个不包含 ii​ 的集合 SS​​ ,设 x=aS,y=aS+2ix=a_S,y=a_{S+2^i}​ ,则有新的 aS=x+y,aS+2i=xya_S=x+y,a_{S+2^i}=x-y​ 。这样我们就以 O(2nn)O(2^nn) 的时间复杂度求出了 FWTFWT​ 。

    1
    2
    3
    4
    5
    6
    7
    8
    for(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;
    }
    }
    }

    FWTFWT​ 的逆运算直接对每一位做逆操作即可。

    时间复杂度 O(2nn)O(2^nn)

  6. 子集卷积

    考虑如果我们做普通的或卷积,那么会有一些 [jk,jk=i][j\wedge k\neq \emptyset,j\vee k=i](j,k)(j,k) 对贡献到 ii 上,所以我们不能直接做或卷积。

    但注意到 [jk=,jk=i][j\wedge k=\emptyset,j\vee k=i] 的条件等价于 [j+k=i,jk=i][|j|+|k|=i,j\vee k=i] ,所以我们可以将所有集合按元素个数分组,将第 xx 组和第 yy 组或卷积得到的集合幂级数中元素个数恰为 x+yx+y 的部分贡献到最终答案中。

    如果我们对所有 O(n2)O(n^2)​ 对 (x,y)(x,y)​​ 暴力做或卷积,复杂度会是 O(2nn3)O(2^nn^3) 。但我们发现这样做对每一组操作了 nnFWTFWT ,这是多余的操作。所以可以事先算出所有组的 FWTFWT ,然后对所有 O(n2)O(n^2)(x,y)(x,y)FWTFWT 数组直接对应位相乘加到答案,最后再把答案的每个组用 FWTFWT 的逆运算操作回去即可。

    时间复杂度 O(2nn2)O(2^nn^2)​ 。

  7. 子集卷积 exp

    考虑按最高位分组,每一组中最多选择一个,所以答案即为所有组子集卷积后的答案。考虑从低位到高位依次合并,合并第 ii​ 位时的复杂度为 O(2ii2)O(2^ii^2) ,总复杂度为 i=1nO(2ii2)=O(2nn2)\sum\limits_{i=1}^n O(2^ii^2)=O(2^nn^2)​ 。

  8. 仍然按最高位分组,且每一组中最多选择一个,从低位到高位合并,但我们要记录当前还有几个要选。记 Gi,jG_{i,j} 表示合并完前 ii 组后还需要再添加 jj 个的方案数。初始即为 G0,i=fiG_{0,i}=f_i ,每次添加一组即为 Gi,j=Gi1,j+Gi1,j1FiG_{i,j}=G_{i-1,j}+G_{i-1,j-1}F_i ,其中 FiF_i 表示第 ii 组形成的集合。总复杂度为 i=1nO(2ii2(ni))=O(2nn2)\sum\limits_{i=1}^nO(2^ii^2(n-i))=O(2^nn^2)

    时间复杂度 O(2nn2)O(2^nn^2)

一些例题

  1. GYM103202M

  2. GYM103109K

  3. CF662C

  4. AGC43C (luogu)

  5. WC2018 (luogu) 州区划分

  6. P5933 带边权连通图个数

  7. loj154 #154. 集合划分计数

欢迎关注我的其它发布渠道