题解: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;
}

浙公网安备 33010602011771号