• 集合幂级数的ln和exp运算及组合意义


    集合幂级数的ln和exp运算及组合意义

    子集卷积

    (f,g,h)为集合幂级数
    定义(h)(f)(g)的子集卷积

    [h_S=sum_{L}sum_{R} f_L g_R [L cap R=emptyset][L cup R=S] ]

    注意到([L cap R=emptyset][L cup R=S]=[|L|+|R|=|S|][L cup R=S])

    我们定义集合占位幂级数 (F_{i,S}),(F_{|S|,S}=f_S),其余情况为0

    那么上面的式子就可以简化为

    [H_{k,S}=sum_isum_jsum_Lsum_R[i+j=k][L cup R=S]F_{i,L} G_{j,R} ]

    容易发现,第一维是一个普通卷积,而第二维是集合幂级数的or卷积. 而集合占位幂级数可以看成一个二元生成函数,每一维之间是独立的,只需要分开即可.(可以考虑多项式的乘法如((xy+x^2y)(2x^2y+3y^2)),感性理解即可)

    那么我们处理这个卷积时,只需要对每一行(f_i)做FMT,然后对于每一列,暴力做普通卷积,再把每一行IFMT回来即可。因为有(n)(2^n)列,所以复杂度(O(n^22^n))

    集合幂级数exp

    我们考虑EGF的组合意义,EGF的exp表示把元素组合成集合的方案数。不妨定义(F_{|S|,S}=frac{f_S}{|S|!}). 那么每一列就是一个EGF. 我们先定义占位集合幂级数的exp为(exp F -1=sum_{igeq 1}frac{F^i}{i!}),其中(F)的乘法定义为子集卷积. 由于占位集合幂级数中中两维的独立性,这等价于对第一维做普通卷积意义下的exp,对第二维做or卷积.

    实现时,类似子集卷积,先对行FMT,再列exp,再IFMT回来即可. 对列做exp,当然可以用多项式全家桶做到(O(nlog n)),但常数爆炸。因为(n)很小,考虑如何(O(n^2))做exp.

    (G=exp F -1)

    两边求导得(G'=F' exp F=F'G)

    写成系数形式(ng_n=sum_{i=0}^{n-1}if_ig_{n-i}),即

    [g_n=frac{1}{n} sum_{i=0}^{n-1}if_ig_{n-i}]

    集合幂级数的exp好像十分抽象,我们现在探讨它的组合意义. 设(g_n)为真实的方案数(没有除掉EGF中的n!)

    [frac{g_n}{n!}=sum_{i=0}^{n-1} frac{i}{n} frac{f_i}{i!} frac{g_{n-i}}{(n-i)!} ]

    [g_n=sum_{i=0}^{n-1} frac{i}{n}inom{n}{i}f_i g_{n-i}=sum_{i=0}^{n-1} inom{n-1}{i-1}f_i g_{n-i} ]

    组合解释如下: 枚举(n)所在的集合的大小(i),那么该集合内部的方案数为(f_i).从的(n-1)个数中选(i-1)个数与(n)放到同一集合。剩下的(n-i)个数随便放,方案数(g_{n-i}). 那么exp的意义就是选取若干个不相交的集合组合成S的方案数

    void exp(int *f,int *g,int n){
    	static int F[maxlogn+5][maxn+5],G[maxlogn+5][maxn+5];
    	for(int i=0;i<(1<<n);i++) for(int j=0;j<=n;j++) F[j][i]=G[j][i]=0;
    	for(int i=0;i<(1<<n);i++) F[cnt[i]][i]=f[i];
    	for(int i=0;i<=n;i++) FMT(F[i],n,1);
    	G[0][0]=1;
    	FMT(G[0],n,1);
    	for(int s=0;s<(1<<n);s++){
    		for(int i=1;i<=n;i++){
    			for(int j=0;j<=i-1;j++) G[i][s]=addm(G[i][s],1ll*F[j][s]*j*G[i-j][s]%mod);
    			G[i][s]=1ll*G[i][s]*invi[i]%mod;
    		}
    	} 
    	for(int i=0;i<=n;i++) FMT(G[i],n,-1);
    	for(int i=0;i<(1<<n);i++) g[i]=G[cnt[i]][i];
    }
    

    定义(G)的生成子图为包含(G)的所有顶点的子图。给出一个图G,求G的连通生成子图数量

    集合幂级数ln

    之前我们定义了(e^G-1=F).同理,我们定义(G=ln(F+1))

    同样,对第一维做普通卷积意义下的ln,对第二维做or卷积.考虑如何(O(n^2))ln.

    我们只需要把exp的式子反过来就好了

    [g_n=frac{1}{n} sum_{i=0}^{n-1}if_ig_{n-i}]

    [f_n=g_n-frac{1}{n}sum_{i=1}^{n-1}if_ig_{n-i} ]

    再把字母换一下就得到了

    [g_n=f_n-frac{1}{n}sum_{i=1}^{n-1}ig_if_{n-i} ]

    由exp的组合意义可知,ln的组合意义为将S拆分成若干个不相交集合的方案数

    void ln(int *f,int *g,int n){
    	static int F[maxlogn+5][maxn+5],G[maxlogn+5][maxn+5];
    	for(int i=0;i<(1<<n);i++) for(int j=0;j<=n;j++) F[j][i]=G[j][i]=0;
    	for(int i=0;i<(1<<n);i++) F[cnt[i]][i]=f[i];
    	for(int i=0;i<=n;i++) FMT(F[i],n,1);
    	G[0][0]=1;
    	FMT(G[0],n,1);
    	for(int s=0;s<(1<<n);s++){
    		for(int i=1;i<=n;i++){
    			for(int j=0;j<=i-1;j++) G[i][s]=addm(G[i][s],1ll*F[i-j][s]*G[j][s]%mod*j%mod);
    			G[i][s]=decm(F[i][s],1ll*G[i][s]*invi[i]%mod)%mod;
    		}
    	} 
    	for(int i=0;i<=n;i++) FMT(G[i],n,-1);
    	for(int i=0;i<(1<<n);i++) g[i]=G[cnt[i]][i];
    }
    

    k-exp

    我们在ln和exp的定义中,都没有限制拆分成集合的个数,如(sum_{i geq 1}frac{F^i}{i!}),i的取值是1到n. 如果限制拆分的个数之多为k,我们就得到了k-exp

    (G=sum_{i=1}^k frac{F^i}{i!})

    同样两边求导,有(G'=F'(G-frac{F^k}{k!})),因为对右边求导后,每一项都变成了前一项,只有第k项消失了

    同样考虑每一列怎么做.对(F^k)做多项式快速幂(即先ln,乘k再exp),设得到的结果为(h).那么上式就可以写成

    (ng_n=sum_{i=0}^{n-1}(n-i)f_{n-i}(g_i-h_i))

    就可以在(O(n^2))的时间内求解

    模板题LOJ 154
    代码

    彩蛋

    版权声明:因为我是蒟蒻,所以请大佬和神犇们不要转载(有坑)的文章,并指出问题,谢谢
  • 相关阅读:
    常用Git代码托管服务分享
    .NET中操作IPicture、IPictureDisp
    Git学习笔记与IntelliJ IDEA整合
    螺旋队列问题
    杂题3道
    .NET 配置文件简单使用
    C++之Effective STL
    不容易理解的 lock 和 merge
    状态模式
    观察者模式
  • 原文地址:https://www.cnblogs.com/birchtree/p/14531986.html
Copyright © 2020-2023  润新知