zoukankan      html  css  js  c++  java
  • 有标号的DAG计数I~IV

    有标号的DAG计数

    最近心血来潮来写一写这个玩意儿。

    请特别注意定义生成函数时下标的起始位置。

    有标号的DAG计数I

    (n)点带标号(DAG)的数量模(10007)(nle5000)

    数据范围显然(O(n^2))。设(f_i)表示答案,枚举(DAG)中入度为零的点的数量(j),方案数为(inom ij),将图拆成两个部分,前(j)个点向后(i-j)个点的连边任意,方案数为(2^{j(i-j)}),后(j)个点构成一张(DAG),方案数为(f_{i-j})

    但是这样并不保证入度为零的点只有(j)个,所以需要容斥。

    [f_i=sum_{j=1}^i(-1)^{j-1}inom ij2^{j(i-j)}f_{i-j} ]

    有标号的DAG计数II

    (n)点带标号(DAG)的数量模(998244353)(nle100000)

    首先(2^{j(i-j)})需要处理一下。需要想办法把(ij)项去掉。

    (j(i-j)=frac{i^2}{2}-frac{j^2}{2}-frac{(i-j)^2}{2})

    好在模(998244353)意义下存在(2)的二次剩余。所以可以把原递推式划一划。

    [f_i=sum_{j=1}^i(-1)^{j-1}frac{i!}{j!(i-j)!}frac{sqrt2^{i^2}}{sqrt2^{j^2}sqrt2^{(i-j)^2}}f_{i-j} ]

    (F(x)=sum_{i=0}^nfrac{f_i}{i!sqrt2^{i^2}}x^i,G(x)=sum_{i=1}^nfrac{(-1)^{i-1}}{i!sqrt2^{i^2}}x^i)

    则有(F(x)=G(x)F(x)+1),故(F(x)=frac{1}{1-G(x)})

    多项式求逆即可。

    有标号的DAG计数III

    (n)点带标号(DAG)的数量模(10007),要求图弱连通。(nle5000)

    首先还是把前面的(f_i)求出来,然后枚举某一个点所在的弱连通(DAG)大小,有递推式:

    [g_i=f_i-sum_{j=1}^{i-1}inom{i-1}{j}f_jg_{i-j} ]

    有标号的DAG计数IV

    (n)点带标号(DAG)的数量模(998244353),要求图弱连通。(nle100000)

    划一下式子:

    [frac{g_i}{(i-1)!}=frac{f_i}{(i-1)!}-sum_{j=1}^{i-1}frac{f_j}{j!}frac{g_{i-j}}{(i-j-1)!} ]

    (F(x)=sum_{i=1}^nfrac{f_i}{i!}x^i,G(x)=sum_{i=1}^nfrac{g_i}{(i-1)!}x^i,H(x)=sum_{i=1}^nfrac{f_i}{(i-1)!}x^i)

    则有(G(x)=H(x)-F(x)G(x)),故(G(x)=frac{H(x)}{1+F(x)})

    以上是方法一。

    方法二非常地有趣。我们发现一个带标号的(DAG)不要求弱连通实质上是由若干个弱连通(DAG)构成的,我们设第二题答案的指数生成函数为(F(x)),第四题答案的指数生成函数为(G(x)),于是就有$$F(x)=e^{G(x)}$$
    也就是$$G(x)=ln F(x)$$

    多项式求(ln)即可。

    code

    有标号的DAG计数I

    #include<cstdio>
    #include<algorithm>
    using namespace std;
    const int N = 5005;
    const int mod = 10007;
    int n,C[N][N],bin[N*N],f[N];
    int main(){
    	scanf("%d",&n);
    	for (int i=C[0][0]=1;i<=n;++i)
    		for (int j=C[i][0]=1;j<=i;++j)
    			C[i][j]=(C[i-1][j]+C[i-1][j-1])%mod;
    	for (int i=bin[0]=1;i<=n*n;++i) bin[i]=(bin[i-1]<<1)%mod;
    	f[0]=1;
    	for (int i=1;i<=n;++i)
    		for (int j=1;j<=i;++j)
    			f[i]=(f[i]+1ll*(j&1?1:mod-1)*bin[j*(i-j)]%mod*C[i][j]%mod*f[i-j])%mod;
    	printf("%d
    ",f[n]);return 0;
    }
    

    有标号的DAG计数II

    #include<cstdio>
    #include<algorithm>
    using namespace std;
    const int N = 3e5+5;
    const int mod = 998244353;
    const int rem = 882049182;
    int n,jc[N],jcn[N],g[N],f[N],rev[N],og[N];
    int fastpow(int a,int b){
    	int res=1;
    	while (b) {if (b&1) res=1ll*res*a%mod;a=1ll*a*a%mod;b>>=1;}
    	return res;
    }
    void init(int len){
    	int l=0;while ((1<<l)<len) ++l;
    	for (int i=0;i<len;++i) rev[i]=(rev[i>>1]>>1)|((i&1)<<l-1);
    }
    void ntt(int *P,int op,int len){
    	for (int i=0;i<len;++i) if (i<rev[i]) swap(P[i],P[rev[i]]);
    	for (int i=1;i<len;i<<=1){
    		int W=fastpow(3,(mod-1)/(i<<1));
    		if (op==-1) W=fastpow(W,mod-2);
    		og[0]=1;for (int j=1;j<i;++j) og[j]=1ll*og[j-1]*W%mod;
    		for (int p=i<<1,j=0;j<len;j+=p)
    			for (int k=0;k<i;++k){
    				int x=P[j+k],y=1ll*og[k]*P[j+k+i]%mod;
    				P[j+k]=(x+y)%mod;P[j+k+i]=(x-y+mod)%mod;
    			}
    	}
    	if (op==-1) for (int i=0,Inv=fastpow(len,mod-2);i<len;++i) P[i]=1ll*P[i]*Inv%mod;
    }
    int tmp[N];
    void Get_Inv(int *a,int *b,int len){
    	if (len==1) {b[0]=fastpow(a[0],mod-2)%mod;return;}
    	Get_Inv(a,b,len>>1);
    	for (int i=0;i<len;++i) tmp[i]=a[i];
    	init(len<<1);ntt(tmp,1,len<<1);ntt(b,1,len<<1);
    	for (int i=0;i<len<<1;++i) tmp[i]=(2ll*b[i]-1ll*b[i]*b[i]%mod*tmp[i]%mod+mod)%mod;
    	ntt(tmp,-1,len<<1);
    	for (int i=0;i<len;++i) b[i]=tmp[i],b[i+len]=0;
    }
    int main(){
    	scanf("%d",&n);g[0]=jc[0]=jcn[0]=1;
    	for (int i=1,inv=fastpow(rem,mod-2);i<=n;++i){
    		jc[i]=1ll*jc[i-1]*i%mod,jcn[i]=fastpow(jc[i],mod-2);
    		g[i]=1ll*(i&1?mod-1:1)*fastpow(inv,1ll*i*i%(mod-1))%mod*jcn[i]%mod;
    	}
    	int len=1;while (len<=n) len<<=1;
    	Get_Inv(g,f,len);
    	printf("%lld
    ",1ll*f[n]*fastpow(rem,1ll*n*n%(mod-1))%mod*jc[n]%mod);
    	return 0;
    }
    

    有标号的DAG计数III

    #include<cstdio>
    #include<algorithm>
    using namespace std;
    const int N = 5005;
    const int mod = 10007;
    int n,bin[N*N],C[N][N],f[N],g[N];
    int main(){
    	scanf("%d",&n);
    	for (int i=C[0][0]=1;i<=n;++i)
    		for (int j=C[i][0]=1;j<=i;++j)
    			C[i][j]=(C[i-1][j]+C[i-1][j-1])%mod;
    	for (int i=bin[0]=1;i<=n*n;++i) bin[i]=(bin[i-1]<<1)%mod;
    	f[0]=g[0]=1;
    	for (int i=1;i<=n;++i){
    		for (int j=1;j<=i;++j)
    			f[i]=(f[i]+1ll*(j&1?1:mod-1)*C[i][j]*bin[j*(i-j)]*f[i-j])%mod;
    		g[i]=f[i];
    		for (int j=1;j<i;++j)
    			g[i]=(g[i]-1ll*C[i-1][j]*f[j]*g[i-j]%mod+mod)%mod;
    	}
    	printf("%d
    ",g[n]);return 0;
    }
    

    有标号的DAG计数IV

    方法一:多项式求逆。

    #include<cstdio>
    #include<algorithm>
    using namespace std;
    const int N = 3e5+5;
    const int mod = 998244353;
    const int rem = 882049182;
    int n,jc[N],jcn[N],g[N],f[N],h[N],rev[N],og[N];
    int fastpow(int a,int b){
    	int res=1;
    	while (b) {if (b&1) res=1ll*res*a%mod;a=1ll*a*a%mod;b>>=1;}
    	return res;
    }
    void init(int len){
    	int l=0;while ((1<<l)<len) ++l;
    	for (int i=0;i<len;++i) rev[i]=(rev[i>>1]>>1)|((i&1)<<l-1);
    }
    void ntt(int *P,int op,int len){
    	for (int i=0;i<len;++i) if (i<rev[i]) swap(P[i],P[rev[i]]);
    	for (int i=1;i<len;i<<=1){
    		int W=fastpow(3,(mod-1)/(i<<1));
    		if (op==-1) W=fastpow(W,mod-2);
    		og[0]=1;for (int j=1;j<i;++j) og[j]=1ll*og[j-1]*W%mod;
    		for (int p=i<<1,j=0;j<len;j+=p)
    			for (int k=0;k<i;++k){
    				int x=P[j+k],y=1ll*og[k]*P[j+k+i]%mod;
    				P[j+k]=(x+y)%mod;P[j+k+i]=(x-y+mod)%mod;
    			}
    	}
    	if (op==-1) for (int i=0,Inv=fastpow(len,mod-2);i<len;++i) P[i]=1ll*P[i]*Inv%mod;
    }
    int tmp[N];
    void Get_Inv(int *a,int *b,int len){
    	if (len==1) {b[0]=fastpow(a[0],mod-2)%mod;return;}
    	Get_Inv(a,b,len>>1);
    	for (int i=0;i<len;++i) tmp[i]=a[i];
    	init(len<<1);ntt(tmp,1,len<<1);ntt(b,1,len<<1);
    	for (int i=0;i<len<<1;++i) tmp[i]=(2ll*b[i]-1ll*b[i]*b[i]%mod*tmp[i]%mod+mod)%mod;
    	ntt(tmp,-1,len<<1);
    	for (int i=0;i<len;++i) b[i]=tmp[i],b[i+len]=tmp[i]=tmp[i+len]=0;
    }
    int main(){
    	scanf("%d",&n);g[0]=jc[0]=jcn[0]=1;
    	for (int i=1,inv=fastpow(rem,mod-2);i<=n;++i){
    		jc[i]=1ll*jc[i-1]*i%mod,jcn[i]=fastpow(jc[i],mod-2);
    		g[i]=1ll*(i&1?mod-1:1)*fastpow(inv,1ll*i*i%(mod-1))%mod*jcn[i]%mod;
    	}
    	int len=1;while (len<=n) len<<=1;
    	Get_Inv(g,f,len);
    	for (int i=0;i<=n;++i){
    		f[i]=1ll*f[i]*fastpow(rem,1ll*i*i%(mod-1))%mod;
    		h[i]=1ll*f[i]*i%mod;g[i]=0;
    	}
    	for (int i=n+1;i<len;++i) f[i]=g[i]=0;
    	Get_Inv(f,g,len);
    	for (int i=n+1;i<len;++i) g[i]=0;
    	init(len);ntt(g,1,len);ntt(h,1,len);
    	for (int i=0;i<len;++i) g[i]=1ll*g[i]*h[i]%mod;
    	ntt(g,-1,len);
    	printf("%lld
    ",1ll*g[n]*jc[n-1]%mod);return 0;
    }
    

    方法二:多项式求(ln)

    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    using namespace std;
    const int N = 3e5+5;
    const int mod = 998244353;
    const int rem = 882049182;
    int n,inv[N],jc[N],jcn[N],g[N],f[N],rev[N],og[N];
    int fastpow(int a,int b){
    	int res=1;
    	while (b) {if (b&1) res=1ll*res*a%mod;a=1ll*a*a%mod;b>>=1;}
    	return res;
    }
    void init(int len){
    	int l=0;while ((1<<l)<len) ++l;
    	for (int i=0;i<len;++i) rev[i]=(rev[i>>1]>>1)|((i&1)<<l-1);
    }
    void ntt(int *P,int op,int len){
    	for (int i=0;i<len;++i) if (i<rev[i]) swap(P[i],P[rev[i]]);
    	for (int i=1;i<len;i<<=1){
    		int W=fastpow(3,(mod-1)/(i<<1));
    		if (op==-1) W=fastpow(W,mod-2);
    		og[0]=1;for (int j=1;j<i;++j) og[j]=1ll*og[j-1]*W%mod;
    		for (int p=i<<1,j=0;j<len;j+=p)
    			for (int k=0;k<i;++k){
    				int x=P[j+k],y=1ll*og[k]*P[j+k+i]%mod;
    				P[j+k]=(x+y)%mod;P[j+k+i]=(x-y+mod)%mod;
    			}
    	}
    	if (op==-1) for (int i=0,Inv=fastpow(len,mod-2);i<len;++i) P[i]=1ll*P[i]*Inv%mod;
    }
    int s1[N];
    void Get_Inv(int *a,int *b,int len){
    	if (len==1) {b[0]=fastpow(a[0],mod-2)%mod;return;}
    	Get_Inv(a,b,len>>1);
    	for (int i=0;i<len;++i) s1[i]=a[i];
    	init(len<<1);ntt(s1,1,len<<1);ntt(b,1,len<<1);
    	for (int i=0;i<len<<1;++i) s1[i]=(2ll*b[i]-1ll*b[i]*b[i]%mod*s1[i]%mod+mod)%mod;
    	ntt(s1,-1,len<<1);
    	for (int i=0;i<len;++i) b[i]=s1[i],b[i+len]=s1[i]=s1[i+len]=0;
    }
    void Get_Der(int *a,int *b,int len){
    	for (int i=1;i<len;++i) b[i-1]=1ll*i*a[i]%mod;
    	b[len-1]=0;
    }
    void Get_Int(int *a,int *b,int len){
    	for (int i=1;i<len;++i) b[i]=1ll*inv[i]*a[i-1]%mod;
    	b[0]=0;
    }
    int s2[N],s3[N];
    void Get_ln(int *a,int *b,int len){
    	Get_Der(a,s2,len);Get_Inv(a,s3,len);
    	init(len<<1);ntt(s2,1,len<<1);ntt(s3,1,len<<1);
    	for (int i=0;i<len<<1;++i) s2[i]=1ll*s2[i]*s3[i]%mod;
    	ntt(s2,-1,len<<1);Get_Int(s2,b,len);
    	for (int i=0;i<len<<1;++i) s2[i]=s3[i]=0;
    }
    int main(){
    	scanf("%d",&n);g[0]=jc[0]=jcn[0]=1;
    	for (int i=1,Inv=fastpow(rem,mod-2);i<=n;++i){
    		inv[i]=i==1?1:1ll*inv[mod%i]*(mod-mod/i)%mod;
    		jc[i]=1ll*jc[i-1]*i%mod,jcn[i]=fastpow(jc[i],mod-2);
    		g[i]=1ll*(i&1?mod-1:1)*fastpow(Inv,1ll*i*i%(mod-1))%mod*jcn[i]%mod;
    	}
    	int len=1;while (len<=n) len<<=1;
    	Get_Inv(g,f,len);
    	for (int i=0;i<=n;++i) f[i]=1ll*f[i]*fastpow(rem,1ll*i*i%(mod-1))%mod;
    	for (int i=n+1;i<len;++i) f[i]=0;
    	memset(g,0,sizeof(g));Get_ln(f,g,len);
    	printf("%lld
    ",1ll*g[n]*jc[n]%mod);
    	return 0;
    }
    
  • 相关阅读:
    iOS YSKit系列
    Xcode $(SRCROOT)和$(PROJECT_DIR)区别
    gcc -ldl 选项作用
    rpm 命令
    ls 命令详解
    Linux下用户组、文件权限详解
    服务不支持chkconfig的解决
    Linux 安装 MongoDB数据库
    CentOS 7 之前好好的,突然一天启动时黑屏,没有登陆界面了(配置 network-scripts 连网)
    vi 命令集
  • 原文地址:https://www.cnblogs.com/zhoushuyu/p/10077241.html
Copyright © 2011-2022 走看看