2023牛客暑期多校训练营8 B Bloodline Counter 指数型生成函数 容斥 多项式求逆

传送门

容易想到求出竞赛图上最大环\(\le k\)的数量,再求出\(\le k-1\)的数量作差即可得到答案。

设指数型生成函数\(G(x)\)表示大小为\(i\)的环的方案数。

\(G(x)=\sum_{i=1}^k\frac{a_i}{i!}x^i\)

那么最大环\(\le k\)的数量\(=[x^n]n!\sum_{i=1}^k i!\frac{(G(x))^i}{i!}\)

这里是枚举整张图的环的个数\(i\),由于会有重复的方案所以要除以\(i!\),又为了固定这\(i\)环之间的拓扑关系所以还需要乘以\(i!\)

\(\sum (G(x))^i=\frac{G(x)-(G(x))^{k+1}}{1-G(x)}=\frac{G(x)}{1-G(x)}\)

问题的关键是求\(G(x)\)

设竞赛图的指数型生成函数为\(F(x)=\sum_{i=1}^{n}2^{C(i,2)}\frac{x^i}{i!}\)

\(G(x)=\sum_{i=1}^n(-1)^{i-1}i!\frac{F(x)}{i!}=\frac{F(x)}{1+F(x)}\)

const int MAXN=1130010,inv2=(mod+1)/2,GG=3;
int n,k,lim;
int f[MAXN],g[MAXN];
int F[MAXN],G[MAXN],h[MAXN],H[MAXN];
int fac[MAXN],inv[MAXN];
int a[MAXN],b[MAXN];
int rev[MAXN];
int ksm(int b,int p)
{
	int cnt=1;
	while(p)
	{
		if(p&1)cnt=(ll)cnt*b%mod;
		p=p>>1;b=(ll)b*b%mod;
	}
	return cnt;
}
void NTT(int *a,int op)
{
	rep(0,lim-1,i)if(i<rev[i])swap(a[i],a[rev[i]]);
	for(int len=2;len<=lim;len=len<<1)
	{
		int mid=len>>1;
		int wn=ksm(GG,op==1?(mod-1)/len:mod-1-(mod-1)/len);
		for(int j=0;j<lim;j+=len)
		{
			int d=1;
			rep(0,mid-1,i)
			{
				int x=a[i+j];
				int y=(ll)a[i+j+mid]*d%mod;
				a[i+j]=(x+y)%mod;
				a[i+j+mid]=(x-y+mod)%mod;
				d=(ll)d*wn%mod;
			}
		}
	}
	if(op==-1)
	{
		int IN=ksm(lim,mod-2);
		rep(0,lim-1,i)a[i]=(ll)a[i]*IN%mod;
	}
}
void qn(int *g,int *f,int n)//求逆
//g会更改 f不会。
{
	if(n==1)
	{
		g[0]=1;//特殊了
		return;
	}
	qn(g,f,(n+1)>>1);
	rep(0,n-1,i)a[i]=g[i],b[i]=f[i];
	lim=1;
	while(lim<=n-1+n-1)lim=lim<<1;
	rep(0,lim-1,i)rev[i]=rev[i>>1]>>1|((i&1)?lim>>1:0);
	NTT(a,1);NTT(b,1);
	rep(0,lim-1,i)a[i]=(ll)a[i]*b[i]%mod*a[i]%mod;
	NTT(a,-1);
	rep(0,lim-1,i)
	{
		if(i<=n-1)g[i]=((ll)2*g[i]-a[i]+mod)%mod;
		a[i]=b[i]=0;
	}
}
void solve(int *A,int *B,int n)
{
	lim=1;
	while(lim<=n+n)lim=lim<<1;
	rep(0,lim-1,i)rev[i]=rev[i>>1]>>1|((i&1)?lim>>1:0);
	NTT(A,1);NTT(B,1);
	rep(0,lim-1,i)A[i]=(ll)A[i]*B[i]%mod;
	//rep(0,lim-1,i)put(a[i]);
	NTT(A,-1);
	//rep(0,lim-1,i)put(a[i]);
	rep(n+1,lim-1,i)A[i]=B[i]=0;
}
signed main() 
{
	freopen("1.in","r",stdin);
	//freopen("1.out","w",stdout);
	sc(n);sc(k);
	fac[0]=1;
	rep(1,n,i)fac[i]=(ll)fac[i-1]*i%mod;
	inv[n]=ksm(fac[n],mod-2);
	fep(n-1,0,i)inv[i]=(ll)inv[i+1]*(i+1)%mod;
	rep(1,n,i)
	{
		int ww=(ll)(i-1)*i/2%(mod-1);
		g[i]=f[i]=(ll)ksm(2,ww)*inv[i]%mod;
	}
	//rep(1,n,i)putl((ll)g[i]*fac[i]%mod);
	f[0]=1;
	qn(F,f,n+1);
	solve(g,F,n);
	/*lim=1;
	while(lim<=n+n)lim=lim<<1;
	rep(0,lim-1,i)rev[i]=rev[i>>1]>>1|((i&1)?lim>>1:0);
	NTT(g,1);NTT(F,1); 
	rep(0,lim-1,i)g[i]=(ll)g[i]*F[i]%mod;
	NTT(g,-1);*/
	//rep(1,n,i)putl((ll)g[i]*fac[i]%mod);
	//rep(1,n,i)putl(i==2?0:fac[i-1]);
	rep(1,k,i)h[i]=f[i]=(mod-g[i])%mod,G[i]=g[i],F[i]=0;
	rep(k+1,n,i)F[i]=g[i]=f[i]=0;
	G[k]=0;h[k]=0;
	h[0]=f[0]=1;
	qn(F,f,n+1);
	qn(H,h,n+1);
	solve(G,H,n);
	solve(g,F,n);
	//rep(1,n,i)putl((ll)g[i]*fac[i]%mod);
	putl((ll)(mod+g[n]-G[n])*fac[n]%mod);
	return 0;
}
posted @ 2023-11-01 21:52  chdy  阅读(27)  评论(0编辑  收藏  举报