【uoj#94】【集训队互测2015】胡策的统计(集合幂级数)
题目传送门:http://uoj.ac/problem/94
这是一道集合幂级数的入门题目。我们先考虑求出每个点集的连通生成子图个数,记为$g_S$,再记$h_S$为点集$S$的生成子图个数,容易发现,$h_S=2^{size_S}$,其中$size_S$为点集$S$的极大生成子图内的边数。特殊的,$f_{\o}=g_{\o}=0$。
定义集合幂级数的乘法为子集卷积,考虑集合幂级数$h$和$g$的关系,我们可以得到
$$h=1+\sum_{k \geq 1}\frac{g^k}{k!}=1+e^h$$
因此可得
$$g=\ln{(1+h)}$$
我们再记$f_S$为点集$S$的生成子图的价值和,可得到
$$f=1+\sum{k \geq 1}\frac{g^k}{k!}k!=\frac{1}{1-g}$$
计算集合幂级数的对数和逆时,因为我们将乘法定义为子集卷积,所以可以将其映射成集合占位幂级数$f_{|S|,S}$后,在将每个集合对应的位看作形式幂级数再暴力求解。
代码:
#include<cstdio> #include<cstring> #include<cmath> #include<cstdlib> #include<algorithm> #define mod 998244353 #define Mod1(x) (x>=mod?x-mod:x) #define Mod2(x) (x<0?x+mod:x) #define ll long long inline ll read() { ll x=0; char c=getchar(),f=1; for(;c<'0'||'9'<c;c=getchar())if(c=='-')f=-1; for(;'0'<=c&&c<='9';c=getchar())x=x*10+c-'0'; return x*f; } inline void write(ll x) { static int buf[20],len; len=0; if(x<0)x=-x,putchar('-'); for(;x;x/=10)buf[len++]=x%10; if(!len)putchar('0'); else while(len)putchar(buf[--len]+'0'); } inline void writeln(ll x){write(x); putchar('\n');} inline void writesp(ll x){write(x); putchar(' ');} int f[25][(1<<20)+5],g[25][(1<<20)+5]; int cnt[(1<<20)+5],inv[25]; int x[410],y[410]; int n,m; inline ll power(ll a,ll b) { ll ans=1; for(;b;b>>=1,a=a*a%mod) if(b&1)ans=ans*a%mod; return ans; } inline void fwt(int* a,int n) { for(int i=1;i<n;i<<=1) for(int j=0;j<n;j+=(i<<1)) for(int k=j;k<j+i;k++) a[k+i]=Mod1(a[k+i]+a[k]); } inline void ifwt(int* a,int n) { for(int i=1;i<n;i<<=1) for(int j=0;j<n;j+=(i<<1)) for(int k=j;k<j+i;k++) a[k+i]=Mod2(a[k+i]-a[k]); } int main() { n=read(); m=read(); for(int i=0;i<m;i++) x[i]=read()-1,y[i]=read()-1; for(int i=1;i<1<<n;i++) cnt[i]=cnt[i>>1]+(i&1); for(int i=1;i<1<<n;i++){ int tot=0; for(int j=0;j<m;j++) if((i&(1<<x[j]))&&(i&(1<<y[j])))++tot; f[cnt[i]][i]=power(2,tot); } for(int i=1;i<=n;i++) fwt(f[i],1<<n); for(int i=1;i<=n;i++) inv[i]=power(i,mod-2); for(int i=1;i<=n;i++){\\求ln for(int k=0;k<i;k++) for(int j=0;j<1<<n;j++) g[i][j]=(g[i][j]+(ll)k*g[k][j]%mod*f[i-k][j])%mod; for(int j=0;j<1<<n;j++) g[i][j]=(f[i][j]+(ll)(mod-inv[i])*g[i][j])%mod; } memset(f,0,sizeof(f)); for(int i=0;i<1<<n;i++) f[0][i]=1; for(int i=1;i<=n;i++)\\求逆 for(int k=0;k<i;k++) for(int j=0;j<1<<n;j++) f[i][j]=(f[i][j]+(ll)f[k][j]*g[i-k][j])%mod; ifwt(f[n],1<<n); writeln(f[n][(1<<n)-1]); return 0; }