• 有标号的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} imes inom{n}{i} imes 2^{i(n-i)} imes 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
    ",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}} imes frac{f[n-i]}{(n-i)!sqrt{2}^{(n-i)^2}})

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

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

    这就是一个$F(x)=F(x) imes 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
    ",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}inom{n-1}{i-1} imes g[i] imes 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
    ",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)!} imes frac{f[n-i]}{(n-i)!})

    也就是这样的形式:

    (G(x)=F(x)-G(x) imes 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
    ",A[N]*fac[N-1]%MOD);
    	return 0;
    }
    
  • 相关阅读:
    cf1100 F. Ivan and Burgers
    cf 1033 D. Divisors
    LeetCode 17. 电话号码的字母组合
    LeetCode 491. 递增的子序列
    LeetCode 459.重复的子字符串
    LeetCode 504. 七进制数
    LeetCode 3.无重复字符的最长子串
    LeetCode 16.06. 最小差
    LeetCode 77. 组合
    LeetCode 611. 有效三角形个数
  • 原文地址:https://www.cnblogs.com/ErkkiErkko/p/10396744.html
Copyright © 2020-2023  润新知