有标号的DAG计数I~IV

有标号的DAG计数I

题目链接

COGS RIP

个人题库

分析

感觉一下做不完,就四道题分开发博客(希望这不是个flag)。

问题很简单,就是求\(n\)个结点的带标号DAG的个数,要求时间复杂度为\(O(n^2)\)

\(f[n]\)\(n\)个结点的带标号DAG的个数。

一个思路是枚举入度为\(0\)的结点的个数,套上一篇博客的容斥计数的公式,可以得到:

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

直接递推即可。

代码

#include <bits/stdc++.h>
#define rin(i,a,b) for(register int i=(a);i<=(b);++i)
#define irin(i,a,b) for(register int i=(a);i>=(b);--i)
#define trav(i,a) for(register int i=head[a];i;i=e[i].nxt)
typedef long long LL;
using std::cin;
using std::cout;
using std::endl;

inline int read(){
	int x=0,f=1;char ch=getchar();
	while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
	while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
	return x*f;
}

const int MAXN=5005;
const LL MOD=10007;

int n;
LL fac[MAXN],invf[MAXN],f[MAXN];

inline LL qpow(LL x,LL y){
	LL ret=1,tt=x%MOD;
	while(y){
		if(y&1) ret=ret*tt%MOD;
		tt=tt*tt%MOD;
		y>>=1;
	}
	return ret;
}

inline LL binom(LL n,LL m){
	if(n<0||m<0||n<m) return 0;
	return fac[n]*invf[n-m]%MOD*invf[m]%MOD;
}

void init(){
	fac[0]=1;
	rin(i,1,n) fac[i]=fac[i-1]*i%MOD;
	invf[n]=qpow(fac[n],MOD-2);
	irin(i,n-1,0) invf[i]=invf[i+1]*(i+1)%MOD;
}

int main(){
	n=read();
	init();
	f[0]=f[1]=1;
	rin(i,2,n){
		LL sgn=-1;
		rin(j,1,i){
			sgn=-sgn;
			f[i]=(f[i]+sgn*binom(i,j)*qpow(2,j*(i-j))%MOD*f[i-j]%MOD+MOD)%MOD;
		}
	}
	printf("%lld\n",f[n]);
	return 0;
}

有标号的DAG计数II

题目链接

个人题库

分析

好吧,几个小时前立的flag现在就拔了。

考虑上一题的那个式子,看看能不能化成卷积的形式。

好吧还真可以,

\[\frac{f[n]}{n!\sqrt{2}^{n^2}}=\sum_{i=1}^{n}\frac{(-1)^{i-1}}{i!\sqrt{2}^{i^2}} \times \frac{f[n-i]}{(n-i)!\sqrt{2}^{(n-i)^2}} \]

化的过程比较麻烦(并不)就不写在这里了。

这里要用到二次剩余,提前算出来就好了。

这就是一个\(F(x)=F(x) \times G(x)+1\)的形式,多项式求逆即可。

时间复杂度为\(O(n \log n)\)

代码

#include <bits/stdc++.h>
#define rin(i,a,b) for(register int i=(a);i<=(b);++i)
#define irin(i,a,b) for(register int i=(a);i>=(b);--i)
#define trav(i,a) for(register int i=head[a];i;i=e[i].nxt)
typedef long long LL;
using std::cin;
using std::cout;
using std::endl;

inline int read(){
	int x=0,f=1;char ch=getchar();
	while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
	while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
	return x*f;
}

const int MAXN=100005;
const int NTT=1048576;
const LL MOD=998244353;
const LL G=3;
const LL IG=332748118;
const LL S=116195171;
const LL IS=557219762;

int N,n,m,len;
LL w[NTT+5],iw[NTT+5];
LL fac[MAXN],invf[MAXN];
LL g[MAXN];
LL rev[MAXN<<2],A[MAXN<<2],B[MAXN<<2];

inline LL qpow(LL x,LL y){
	LL ret=1,tt=x%MOD;
	while(y){
		if(y&1) ret=ret*tt%MOD;
		tt=tt*tt%MOD;
		y>>=1;
	}
	return ret;
}

void ntt(LL *c,int dft){
	rin(i,0,n-1)
		if(i<rev[i])
			std::swap(c[i],c[rev[i]]);
	for(register int mid=1;mid<n;mid<<=1){
		int r=(mid<<1),u=NTT/r;
		for(register int l=0;l<n;l+=r){
			int v=0;
			for(register int i=0;i<mid;++i,v+=u){
				LL x=c[l+i],y=c[l+mid+i]*(dft>0?w[v]:iw[v])%MOD;
				c[l+i]=x+y<MOD?x+y:x+y-MOD;
				c[l+mid+i]=x-y>=0?x-y:x-y+MOD;
			}
		}
	}
	if(dft<0){
		LL invn=qpow(n,MOD-2);
		rin(i,0,n-1) c[i]=c[i]*invn%MOD;
	}
}

void getinv(int mdx){
	if(mdx==1){
		A[0]=qpow(g[0],MOD-2);
		return;
	}
	getinv((mdx+1)>>1);
	m=(mdx-1)+((((mdx+1)>>1)-1)<<1),len=0;
	for(n=1;n<=m;n<<=1) ++len;
	rin(i,1,n-1) rev[i]=((rev[i>>1]>>1)|((i&1)<<(len-1)));
	rin(i,0,n-1) B[i]=i<mdx?g[i]:0;
	ntt(A,1);ntt(B,1);
	rin(i,0,n-1) A[i]=(2*A[i]-B[i]*A[i]%MOD*A[i]%MOD+MOD)%MOD;
	ntt(A,-1);
	rin(i,mdx,n-1) A[i]=0;
}

void init(){
	LL v=qpow(G,(MOD-1)/NTT),iv=qpow(IG,(MOD-1)/NTT);
	w[0]=iw[0]=1;
	rin(i,1,NTT-1) w[i]=w[i-1]*v%MOD,iw[i]=iw[i-1]*iv%MOD;
	fac[0]=1;
	rin(i,1,N) fac[i]=fac[i-1]*i%MOD;
	invf[N]=qpow(fac[N],MOD-2);
	irin(i,N-1,0) invf[i]=invf[i+1]*(i+1)%MOD;
}

int main(){
	N=read();
	init();
	g[0]=0;
	LL sgn=-1;
	rin(i,1,N){
		sgn=-sgn;
		g[i]=(sgn*invf[i]*qpow(IS,1ll*i*i)%MOD+MOD)%MOD;
	}
	g[0]=1;
	rin(i,1,N){
		g[i]=(MOD-g[i])%MOD;
	}
	getinv(N+1);
	printf("%lld\n",A[N]*fac[N]%MOD*qpow(S,1ll*N*N)%MOD);
	return 0;
}

有标号的DAG计数III

题目链接

个人题库

分析

要求DAG弱连通。

我们先需要递推出DAGCNT1的\(f\)数组,然后从所有的DAG中减去不满足弱连通的。

类比城市规划那道题的思路,考虑枚举\(1\)号结点所在弱连通块的大小,可以得到状态转移方程:

\[g[n]=f[n]-\sum_{i=1}^{n-1}\binom{n-1}{i-1} \times g[i] \times f[n-i] \]

其中\(g[n]\)表示\(n\)个结点的带标号弱连通DAG的个数。

时间复杂度为\(O(n^2)\)

代码

#include <bits/stdc++.h>
#define rin(i,a,b) for(register int i=(a);i<=(b);++i)
#define irin(i,a,b) for(register int i=(a);i>=(b);--i)
#define trav(i,a) for(register int i=head[a];i;i=e[i].nxt)
typedef long long LL;
using std::cin;
using std::cout;
using std::endl;

inline int read(){
	int x=0,f=1;char ch=getchar();
	while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
	while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
	return x*f;
}

const int MAXN=5005;
const LL MOD=998244353;

int n;
LL fac[MAXN],invf[MAXN],pow2[MAXN*MAXN/4],f[MAXN],g[MAXN];

inline LL qpow(LL x,LL y){
	LL ret=1,tt=x%MOD;
	while(y){
		if(y&1) ret=ret*tt%MOD;
		tt=tt*tt%MOD;
		y>>=1;
	}
	return ret;
}

inline LL binom(LL n,LL m){
	if(n<0||m<0||n<m) return 0;
	return fac[n]*invf[n-m]%MOD*invf[m]%MOD;
}

void init(){
	fac[0]=1;
	rin(i,1,n) fac[i]=fac[i-1]*i%MOD;
	invf[n]=qpow(fac[n],MOD-2);
	irin(i,n-1,0) invf[i]=invf[i+1]*(i+1)%MOD;
	int lim=(n/2)*(n/2+1);pow2[0]=1;
	rin(i,1,lim) pow2[i]=(pow2[i-1]<<1)%MOD;
}

int main(){
	n=read();
	init();
	f[0]=f[1]=1;
	rin(i,2,n){
		LL sgn=-1;
		rin(j,1,i){
			sgn=-sgn;
			f[i]=(f[i]+sgn*binom(i,j)*pow2[j*(i-j)]%MOD*f[i-j]%MOD+MOD)%MOD;
		}
	}
	g[0]=g[1]=1;
	rin(i,2,n){
		rin(j,1,i-1){
			g[i]=(g[i]+binom(i-1,j-1)*g[j]%MOD*f[i-j])%MOD;
		}
		g[i]=(f[i]-g[i]+MOD)%MOD;
	}
	printf("%lld\n",g[n]);
	return 0;
}

有标号的DAG计数IV

题目链接

个人题库

分析

上一个式子可以化成这样:

\[\frac{g[n]}{(n-1)!}=\frac{f[n]}{(n-1)!}-\sum_{i=1}^{n-1}\frac{g[i]}{(i-1)!} \times \frac{f[n-i]}{(n-i)!} \]

也就是这样的形式:

\[G(x)=F(x)-G(x) \times H(x) \]

\[G(x)=\frac{F(x)}{H(x)+1} \]

多项式求逆就好了,时间复杂度为\(O(n \log n)\)

话说多项式的常数项是真玄学。

还有一个非常神仙的多项式求\(\ln\)的做法,可以看这篇博客

代码

#include <bits/stdc++.h>
#define rin(i,a,b) for(register int i=(a);i<=(b);++i)
#define irin(i,a,b) for(register int i=(a);i>=(b);--i)
#define trav(i,a) for(register int i=head[a];i;i=e[i].nxt)
typedef long long LL;
using std::cin;
using std::cout;
using std::endl;

inline int read(){
	int x=0,f=1;char ch=getchar();
	while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
	while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
	return x*f;
}

const int MAXN=100005;
const int NTT=1048576;
const LL MOD=998244353;
const LL G=3;
const LL IG=332748118;
const LL S=116195171;
const LL IS=557219762;

int N,n,m,len;
LL w[NTT+5],iw[NTT+5];
LL f[MAXN],g[MAXN],h[MAXN];
LL fac[MAXN],invf[MAXN];
int rev[MAXN<<2];
LL A[MAXN<<2],B[MAXN<<2];

inline LL qpow(LL x,LL y){
	LL ret=1,tt=x%MOD;
	while(y){
		if(y&1) ret=ret*tt%MOD;
		tt=tt*tt%MOD;
		y>>=1;
	}
	return ret;
}

void ntt(LL *c,int dft){
	rin(i,0,n-1)
		if(i<rev[i])
			std::swap(c[i],c[rev[i]]);
	for(register int mid=1;mid<n;mid<<=1){
		int r=(mid<<1),u=NTT/r;
		for(register int l=0;l<n;l+=r){
			int v=0;
			for(register int i=0;i<mid;++i,v+=u){
				LL x=c[l+i],y=c[l+mid+i]*(dft>0?w[v]:iw[v])%MOD;
				c[l+i]=(x+y<MOD?x+y:x+y-MOD);
				c[l+mid+i]=(x-y>=0?x-y:x-y+MOD);
			}
		}
	}
	if(dft<0){
		LL invn=qpow(n,MOD-2);
		rin(i,0,n-1) c[i]=c[i]*invn%MOD;
	}
}

void getinv(LL *c,int mdx){
	if(mdx==1){
		A[0]=qpow(c[0],MOD-2);
		return;
	}
	getinv(c,(mdx+1)>>1);
	m=(mdx-1)+((((mdx+1)>>1)-1)<<1);
	for(n=1,len=0;n<=m;n<<=1,++len);
	rin(i,1,n-1) rev[i]=((rev[i>>1]>>1)|((i&1)<<(len-1)));
	rin(i,0,n-1) B[i]=i<mdx?c[i]:0;
	ntt(A,1);ntt(B,1);
	rin(i,0,n-1) A[i]=(2*A[i]-B[i]*A[i]%MOD*A[i]%MOD+MOD)%MOD;
	ntt(A,-1);
	rin(i,mdx,n-1) A[i]=0;
}

void init(){
	fac[0]=1;
	rin(i,1,N) fac[i]=fac[i-1]*i%MOD;
	invf[N]=qpow(fac[N],MOD-2);
	irin(i,N-1,0) invf[i]=invf[i+1]*(i+1)%MOD;
	LL v=qpow(G,(MOD-1)/NTT),iv=qpow(IG,(MOD-1)/NTT);w[0]=iw[0]=1;
	rin(i,1,NTT-1) w[i]=w[i-1]*v%MOD,iw[i]=iw[i-1]*iv%MOD;
}

int main(){
	N=read();
	init();
	g[0]=0;
	LL sgn=-1;
	rin(i,1,N){
		sgn=-sgn;
		g[i]=(sgn*invf[i]*qpow(IS,1ll*i*i)%MOD+MOD)%MOD;
	}
	g[0]=1;
	rin(i,1,N) g[i]=(MOD-g[i])%MOD;
	getinv(g,N+1);
	rin(i,1,N) A[i]=A[i]*fac[i]%MOD*qpow(S,1ll*i*i)%MOD,f[i]=A[i]*invf[i-1]%MOD,h[i]=A[i]*invf[i]%MOD;
	f[0]=0;
	memset(A,0,sizeof A);
	h[0]=1;
	getinv(h,N+1);
	m=(N<<1);
	for(n=1,len=0;n<=m;n<<=1,++len);
	rin(i,1,n-1) rev[i]=((rev[i>>1]>>1)|((i&1)<<(len-1)));
	rin(i,0,n-1) B[i]=i<=N?f[i]:0;
	ntt(A,1);ntt(B,1);
	rin(i,0,n-1) A[i]=A[i]*B[i]%MOD;
	ntt(A,-1);
	printf("%lld\n",A[N]*fac[N-1]%MOD);
	return 0;
}

posted on 2019-02-18 17:20  ErkkiErkko  阅读(415)  评论(0编辑  收藏  举报