题解:P12232 【模板】集合幂级数求逆

其实是半在线子集卷积模板。

前置知识

子集卷积。

算法简介

半在线子集卷积用来处理形如 \(f_S=\sum\limits_{T\subset S}f_Tg_{S-T}\) 的卷积。

与子集卷积相同地,我们设 \(F_{i,S}=[|S|=i]f_S,G_{i,S}=[|S|=i]g_S\),那么有 \(F_{i,S}=\sum\limits_{T\subset S}F_{|T|,T}G_{|S-T|,S-T}\)。然后因为对于任意的 \(i\)\(F_{i,S},G_{i,S}\) 都只有 \(|S|=i\) 的位置有值,所以直接对于每个 \(j<i\),把 \(F_j\)\(G_{i-j}\) 做 or 卷积加到 \(F_i\) 上就可以了。

复杂度分析

对于每个 \(i\)\(F_{i}\) 需要 \(O(2^n)\) 时间得到,需要 \(O(n)\) 次 FWT,总复杂度 \(O(2^nn^2)\)居然和普通子集卷积的复杂度一样!

思路

直接看题目给的式子 \(F(x)G(x)=1\)。设 \(f_S=[x^S]F(x),g_S=[x^S]G(x)\),则有

\[\sum\limits_{T\subseteq S}f_Tg_{S-T}=[S\not=\varnothing] \]

\(S=\varnothing\),可得 \(g_\varnothing f_\varnothing=1\)

现在设 \(S\not=\varnothing\),则:

\[\begin{aligned} &\sum\limits_{T\subseteq S}f_Tg_{S-T}=\sum\limits_{T\subseteq S,T\not=\varnothing}f_Tg_{S-T}+f_\varnothing g_S=0\\ &g_S=-\frac{1}{f_\varnothing}\sum\limits_{T\subseteq S,T\not=\varnothing}f_Tg_{S-T} \end{aligned} \]

然后就是一个半在线子集卷积。

代码

```cpp
#include <bits/stdc++.h>
using namespace std;
constexpr int N=20,mod=998244353;
int n,f[N+1][1<<N],g[N+1][1<<N];
long long qpow(long long x,int y=mod-2){
    long long ans=1;
    for(;y;y>>=1,x=x*x%mod)if(y&1)ans=ans*x%mod;
    return ans;
}
void FWT(int *f,int n,int rev=0){
    for(int w=1;w<n;w<<=1)for(int i=0;i<n;i+=w<<1)for(int j=0;j<w;j++)
        if(rev)f[i+j+w]-=f[i+j],f[i+j+w]<0&&(f[i+j+w]+=mod);
        else f[i+j+w]+=f[i+j],f[i+j+w]>=mod&&(f[i+j+w]-=mod);
}
signed main(){
    clock_t _st=clock();
    ios::sync_with_stdio(false);
    cin.tie(0),cout.tie(0);
    cin>>n;
    for(int i=0;i<1<<n;i++)cin>>f[__builtin_popcount(i)][i];
    g[0][0]=qpow(f[0][0]);
    FWT(g[0],1<<n),FWT(f[0],1<<n);
    for(int i=1,inv=mod-g[0][0];i<=n;i++){
        FWT(f[i],1<<n);
        for(int j=1;j<=i;j++)for(int s=0;s<1<<n;s++)
            g[i][s]=(g[i][s]+1ll*g[i-j][s]*f[j][s])%mod;
        for(int s=0;s<1<<n;s++)g[i][s]=1ll*g[i][s]*inv%mod;
    }
    for(int i=0;i<=n;i++)FWT(g[i],1<<n,1);
    for(int i=0;i<1<<n;i++)cout<<g[__builtin_popcount(i)][i]<<' ';cout<<'\n';
    clock_t _ed=clock();
    cerr<<(_ed-_st)*1.0/CLOCKS_PER_SEC<<'\n';
    return 0;
}
posted @ 2026-02-28 13:34  Redolent  阅读(2)  评论(0)    收藏  举报