有標號的DAG計數(FFT)


有標號的DAG計數系列

有標號的DAG計數I

題意

給定一正整數\(n\),對\(n\)個點有標號的有向無環圖(可以不連通)進行計數,輸出答案\(mod \ 10007\)的結果。\(n\le 5000\)

題解

顯然是\(O(n^2)\)來做。
\(f(i)\)表示\(i\)個點有標號的有向無環圖的個數。而\(DAG\)中的特殊點顯然只有兩種,要么是出度為\(0\),要么入度為\(0\)。隨便枚舉哪一種都行,這里枚舉入度為\(0\)的點。
那么得到式子:

\[f(n)=\sum_{i=1}^nC_n^if(n-i)2^{i(n-i)} \]

顯然這樣子枚舉出來的並不是入度個數恰好為枚舉個數的答案,所以需要容斥,也就是:

\[f(i)=\sum_{j=1}^n(-1)^{j-1}C_i^jf(i-j)2^{j(i-j)} \]

\(O(n^2)\)暴力\(dp\)即可。

有標號的DAG計數II

題意

給定一正整數\(n\),對\(n\)個點有標號的有向無環圖(可以不連通)進行計數,輸出答案\(mod \ 998244353\)的結果。\(n\le 10^5\)

題解

推導同上題式子。考慮接着拆。
首先把\(2^{j(i-j)}\)給拆了,\(j(i-j)=i^2/2-j^2/2-(i-j)^2/2\)
這樣子令\(t=\sqrt 2\),也就是\(2\)在模意義下的二次剩余。
接着拆式子:

\[\begin{aligned} f(i)&=\sum_{j=1}^n(-1)^{j-1}C_i^jf(i-j)2^{j(i-j)}\\ &=\sum_{j=1}^n(-1)^{j-1}\frac{i!}{j!(i-j)!}f(i-j)t^{i^2-j^2-(i-j)^2}\\ &=\sum_{j=1}^n \frac{(-1)^{j-1}}{j!t^{j^2}}*\frac{f(i-j)}{(i-j)!t^{(i-j)^2}}*i!*t^{i^2}\\ \frac{f(i)}{i!t^{i^2}}&=\sum_{j=1}^n \frac{(-1)^{j-1}}{j!t^{j^2}}*\frac{f(i-j)}{(i-j)!t^{(i-j)^2}} \end{aligned} \]

\(F(x)\)\(\frac{f(i)}{i!t^{i^2}}\)的生成函數,\(G(x)\)\(\frac{(-1)^{i-1}}{i!t^{i^2}}\)的生成函數,那么上面的等式寫成:\(F(x)=G(x)F(x)+1\),因為卷積之后漏掉了\(F(0)\)
那么解出\(F(x)=\frac{1}{1-G(x)}\),多項式求逆即可。

有標號的DAG計數III

題意

給定一正整數\(n\),對\(n\)個點有標號的有向無環圖進行計數,這里加一個限制:此圖必須是弱連通圖。輸出答案\(mod\ 10007\)的結果。\(n\le 5000\)

題解

又回到了一個\(O(n^2)\)的做法。設答案為\(g(i)\)。那么考慮這里和前面的區別在哪兒?這里要求弱連通。那么連通和不連通的考慮方法一般就是總數減去不合法,那么考慮什么東西是不合法的。

\[g(i)=f(i)-\sum_{j=1}^{i-1}C_{i-1}^{j-1}g(j)f(i-j) \]

為啥呢?因為一旦不是弱連通了,那么必定有兩個以上的聯通塊。我們枚舉其中的一個包含\(1\)號點的聯通塊大小,那么這個方案數是\(g(j)\),而其他點隨意構成\(DAG\),這樣就是上述的式子了。
所以把\(I\)的代碼蒯過來再加一個\(dp\)\(g\)就好了。

有標號的DAG計數IV

題意

給定一正整數\(n\),對\(n\)個點有標號的有向無環圖進行計數,這里加一個限制:此圖必須是弱連通圖。輸出答案\(mod\ 998244353\)的結果。

題解

拆式子啊QwQ。

\[\begin{aligned} g(i)&=f(i)-\sum_{j=1}^{i-1}C_{i-1}^{j-1}g(j)f(i-j)\\ &=f(i)-\sum_{j=1}^{i-1}\frac{(i-1)!}{(j-1)!(i-j)!}g(j)f(i-j)\\ \frac{g(i)}{(i-1)!}&=\frac{f(i)}{(i-1)!}-\sum_{j=1}^{i-1}\frac{g(j)}{(j-1)!}\frac{f(i-j)}{(i-j)!} \end{aligned} \]

這樣子可以構建三個生成函數
\(G(x)=\sum \frac{g(i)}{(i-1)!}x^i\)\(F(x)=\sum \frac{f(i)}{(i-1)!}x^i\)\(H(x)=\sum \frac{f(i)}{i!}x^i\)。其中\(\sum\)都表示\(\sum_{i=1}^{\infty}\)
卷積一下就是\(G(x)=F(x)-G(x)*H(x)\)
化簡一下就是\(G(x)=\frac{F(x)}{1+H(x)}\)
多項式求逆即可。

I

#include<iostream>
#include<cstdio>
using namespace std;
#define MOD 10007
#define MAX 5050
int n,f[MAX],bin[MAX*MAX],jc[MAX],jv[MAX],inv[MAX];
int C(int n,int m){return jc[n]*jv[m]%MOD*jv[n-m]%MOD;}
void add(int &x,int y){x+=y;if(x>=MOD)x-=MOD;}
int main()
{
	scanf("%d",&n);f[0]=jc[0]=jv[0]=inv[0]=inv[1]=bin[0]=1;
	for(int i=2;i<=n;++i)inv[i]=inv[MOD%i]*(MOD-MOD/i)%MOD;
	for(int i=1;i<=n;++i)jc[i]=jc[i-1]*i%MOD;
	for(int i=1;i<=n;++i)jv[i]=jv[i-1]*inv[i]%MOD;
	for(int i=1;i<=n*n;++i)bin[i]=2*bin[i-1]%MOD;
	for(int i=1;i<=n;++i)
		for(int j=1;j<=i;++j)
		{
			int tmp=C(i,j)*f[i-j]%MOD*bin[j*(i-j)]%MOD;
			if(j&1)add(f[i],tmp);else add(f[i],MOD-tmp);
		}
	printf("%d\n",f[n]);
	return 0;
}

II

#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
#define MOD 998244353
#define REM 882049182
#define MAX 300000
int fpow(int a,int b)
{
	int s=1;
	while(b){if(b&1)s=1ll*s*a%MOD;a=1ll*a*a%MOD;b>>=1;}
	return s;
}
int r[MAX],W[MAX];
void NTT(int *P,int opt,int N)
{
	int l=0;for(int i=1;i<N;i<<=1)++l;
	for(int i=0;i<N;++i)r[i]=(r[i>>1]>>1)|((i&1)<<(l-1));
	for(int i=0;i<N;++i)if(i<r[i])swap(P[i],P[r[i]]);
	for(int i=1;i<N;i<<=1)
	{
		int w=fpow(3,(MOD-1)/(i<<1));W[0]=1;
		for(int k=1;k<i;++k)W[k]=1ll*W[k-1]*w%MOD;
		for(int p=i<<1,j=0;j<N;j+=p)
			for(int k=0;k<i;++k)
			{
				int X=P[j+k],Y=1ll*W[k]*P[i+j+k]%MOD;
				P[j+k]=(X+Y)%MOD;P[i+j+k]=(X+MOD-Y)%MOD;
			}
	}
	if(opt==-1)
	{
		reverse(&P[1],&P[N]);
		for(int i=0,inv=fpow(N,MOD-2);i<N;++i)P[i]=1ll*P[i]*inv%MOD;
	}
}
int A[MAX],B[MAX];
void Inv(int *a,int *b,int len)
{
	if(len==1){b[0]=fpow(a[0],MOD-2);return;}
	Inv(a,b,len>>1);
	for(int i=0;i<len;++i)A[i]=a[i],B[i]=b[i];
	NTT(A,1,len<<1);NTT(B,1,len<<1);
	for(int i=0;i<len<<1;++i)A[i]=1ll*A[i]*B[i]%MOD*B[i]%MOD;
	NTT(A,-1,len<<1);
	for(int i=0;i<len;++i)b[i]=(b[i]+b[i])%MOD;
	for(int i=0;i<len;++i)b[i]=(b[i]+MOD-A[i])%MOD;
	for(int i=0;i<len<<1;++i)A[i]=B[i]=0;
}
int jc[MAX],jv[MAX],inv[MAX];
int N,n,F[MAX],G[MAX];
int main()
{	
	scanf("%d",&n);jc[0]=jv[0]=inv[0]=inv[1]=1;
	for(int i=1;i<=n;++i)jc[i]=1ll*jc[i-1]*i%MOD;
	for(int i=2;i<=n;++i)inv[i]=1ll*inv[MOD%i]*(MOD-MOD/i)%MOD;
	for(int i=1;i<=n;++i)jv[i]=1ll*jv[i-1]*inv[i]%MOD;
	for(N=1;N<=n;N<<=1);G[0]=1;int t=fpow(REM,MOD-2);
	for(int i=1;i<N;++i)G[i]=1ll*jv[i]*fpow(t,1ll*i*i%(MOD-1))%MOD;
	for(int i=1;i<N;++i)if(i&1)G[i]=MOD-G[i];
	Inv(G,F,N);
	printf("%lld\n",1ll*F[n]*jc[n]%MOD*fpow(REM,1ll*n*n%(MOD-1))%MOD);
	return 0;
}

III

#include<iostream>
#include<cstdio>
using namespace std;
#define MOD 10007
#define MAX 5050
int n,f[MAX],g[MAX],bin[MAX*MAX],jc[MAX],jv[MAX],inv[MAX];
int C(int n,int m){return jc[n]*jv[m]%MOD*jv[n-m]%MOD;}
void add(int &x,int y){x+=y;if(x>=MOD)x-=MOD;}
int main()
{
	scanf("%d",&n);f[0]=jc[0]=jv[0]=inv[0]=inv[1]=bin[0]=1;
	for(int i=2;i<=n;++i)inv[i]=inv[MOD%i]*(MOD-MOD/i)%MOD;
	for(int i=1;i<=n;++i)jc[i]=jc[i-1]*i%MOD;
	for(int i=1;i<=n;++i)jv[i]=jv[i-1]*inv[i]%MOD;
	for(int i=1;i<=n*n;++i)bin[i]=2*bin[i-1]%MOD;
	for(int i=1;i<=n;++i)
		for(int j=1;j<=i;++j)
		{
			int tmp=C(i,j)*f[i-j]%MOD*bin[j*(i-j)]%MOD;
			if(j&1)add(f[i],tmp);else add(f[i],MOD-tmp);
		}
	for(int i=0;i<=n;++i)g[i]=f[i];
	for(int i=1;i<=n;++i)
		for(int j=1;j<i;++j)
			add(g[i],MOD-C(i-1,j-1)*g[j]%MOD*f[i-j]%MOD);
	printf("%d\n",g[n]);
	return 0;
}

IV

#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
#define MOD 998244353
#define REM 882049182
#define MAX 300000
int fpow(int a,int b)
{
	int s=1;
	while(b){if(b&1)s=1ll*s*a%MOD;a=1ll*a*a%MOD;b>>=1;}
	return s;
}
int r[MAX],W[MAX];
void NTT(int *P,int opt,int N)
{
	int l=0;for(int i=1;i<N;i<<=1)++l;
	for(int i=0;i<N;++i)r[i]=(r[i>>1]>>1)|((i&1)<<(l-1));
	for(int i=0;i<N;++i)if(i<r[i])swap(P[i],P[r[i]]);
	for(int i=1;i<N;i<<=1)
	{
		int w=fpow(3,(MOD-1)/(i<<1));W[0]=1;
		for(int k=1;k<i;++k)W[k]=1ll*W[k-1]*w%MOD;
		for(int p=i<<1,j=0;j<N;j+=p)
			for(int k=0;k<i;++k)
			{
				int X=P[j+k],Y=1ll*W[k]*P[i+j+k]%MOD;
				P[j+k]=(X+Y)%MOD;P[i+j+k]=(X+MOD-Y)%MOD;
			}
	}
	if(opt==-1)
	{
		reverse(&P[1],&P[N]);
		for(int i=0,inv=fpow(N,MOD-2);i<N;++i)P[i]=1ll*P[i]*inv%MOD;
	}
}
int A[MAX],B[MAX];
void Inv(int *a,int *b,int len)
{
	if(len==1){b[0]=fpow(a[0],MOD-2);return;}
	Inv(a,b,len>>1);
	for(int i=0;i<len;++i)A[i]=a[i],B[i]=b[i];
	NTT(A,1,len<<1);NTT(B,1,len<<1);
	for(int i=0;i<len<<1;++i)A[i]=1ll*A[i]*B[i]%MOD*B[i]%MOD;
	NTT(A,-1,len<<1);
	for(int i=0;i<len;++i)b[i]=(b[i]+b[i])%MOD;
	for(int i=0;i<len;++i)b[i]=(b[i]+MOD-A[i])%MOD;
	for(int i=0;i<len<<1;++i)A[i]=B[i]=0;
}
int jc[MAX],jv[MAX],inv[MAX];
int N,n,F[MAX],G[MAX],H[MAX],P[MAX];
int main()
{	
	scanf("%d",&n);jc[0]=jv[0]=inv[0]=inv[1]=1;
	for(int i=1;i<=n;++i)jc[i]=1ll*jc[i-1]*i%MOD;
	for(int i=2;i<=n;++i)inv[i]=1ll*inv[MOD%i]*(MOD-MOD/i)%MOD;
	for(int i=1;i<=n;++i)jv[i]=1ll*jv[i-1]*inv[i]%MOD;
	for(N=1;N<=n;N<<=1);G[0]=1;int t=fpow(REM,MOD-2);
	for(int i=1;i<N;++i)G[i]=1ll*jv[i]*fpow(t,1ll*i*i%(MOD-1))%MOD;
	for(int i=1;i<N;++i)if(i&1)G[i]=MOD-G[i];
	Inv(G,F,N);
	for(int i=1;i<N;++i)F[i]=1ll*F[i]*jc[i]%MOD*fpow(REM,1ll*i*i%(MOD-1))%MOD;
	for(int i=1;i<N;++i)H[i]=1ll*F[i]*jv[i]%MOD;H[0]=1;
	for(int i=1;i<N;++i)F[i]=1ll*F[i]*jv[i-1]%MOD;F[0]=0;
	Inv(H,P,N);
	NTT(F,1,N<<1);NTT(P,1,N<<1);
	for(int i=0;i<N<<1;++i)G[i]=1ll*F[i]*P[i]%MOD;
	NTT(G,-1,N<<1);
	printf("%lld\n",1ll*G[n]*jc[n-1]%MOD);
	return 0;
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM