青春野狼不做小恶魔学妹的梦 - 斯特林数 - 多项式理论

题目大意:
对所 n n n个点的连通图 G \mathrm{G} G求边数的 k k k次方和。
n ≤ 5 × 1 0 4 , k ≤ 15 n\le5\times10^4,k\le15 n5×104,k15
题解:
显然考虑斯特林数: m k = ∑ i = 0 k S ( k , i ) i ! ( m i ) m^k=\sum_{i=0}^kS(k,i)i!\binom mi mk=i=0kS(k,i)i!(im),因此统计 n n n个点的图并选定其中恰好 i i i条不同的边的方案数。
那么设 f m , n f_{m,n} fm,n表示连通图的情况, g m , n g_{m,n} gm,n表示所有图的情况,那么显然 g m , n = ( ( n 2 ) m ) 2 ( n 2 ) − m g_{m,n}=\binom{\binom n2}{m}2^{\binom n2-m} gm,n=(m(2n))2(2n)m,然后枚举1所在连通块大小:
∑ j = 0 m ∑ i = 1 n ( n − 1 i − 1 ) f j , i g m − j , n − i = g m , n \sum_{j=0}^m\sum_{i=1}^n\binom{n-1}{i-1}f_{j,i}g_{m-j,n-i}=g_{m,n} j=0mi=1n(i1n1)fj,igmj,ni=gm,n
那么令
[ x n ] F m ( x ) = f m , n ( n − 1 ) ! [ x n ] G m ( x ) = g m , n n ! [ x n ] H m ( x ) = g m , n ( n − 1 ) ! [x^n]F_m(x)=\frac{f_{m,n}}{(n-1)!}\\ [x^n]G_m(x)=\frac{g_{m,n}}{n!}\\ [x^n]H_m(x)=\frac{g_{m,n}}{(n-1)!} [xn]Fm(x)=(n1)!fm,n[xn]Gm(x)=n!gm,n[xn]Hm(x)=(n1)!gm,n
则:
∑ j = 0 m F j ( x ) G m − j ( x ) = H m ( x ) \sum_{j=0}^mF_j(x)G_{m-j}(x)=H_m(x) j=0mFj(x)Gmj(x)=Hm(x)
因此:
F m ( x ) = H m ( x ) − ∑ j = 0 m − 1 F j ( x ) G m − j ( x ) G 0 ( x ) F_m(x)=\frac{H_m(x)-\sum_{j=0}^{m-1}F_j(x)G_{m-j}(x)}{G_0(x)} Fm(x)=G0(x)Hm(x)j=0m1Fj(x)Gmj(x)
时间复杂度 O ( n k 2 + n k lg ⁡ n ) O(nk^2+nk\lg n) O(nk2+nklgn)
顺便重新屯了一波板子(求逆指对)。

#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define Rep(i,v) rep(i,0,(int)v.size()-1)
#define lint long long
#define p 998244353
#define ull unsigned lint
#define db long double
#define pb push_back
#define mp make_pair
#define fir first
#define sec second
#define gc getchar()
#define debug(x) cerr<<#x<<"="<<x
#define sp <<" "
#define ln <<endl
using namespace std;
typedef pair<int,int> pii;
typedef set<int>::iterator sit;
inline int inn()
{
	int x,ch;while((ch=gc)<'0'||ch>'9');
	x=ch^'0';while((ch=gc)>='0'&&ch<='9')
		x=(x<<1)+(x<<3)+(ch^'0');return x;
}
inline int fast_pow(int x,int k,int ans=1) { for(k<0?k+=p-1:0;k;k>>=1,x=(lint)x*x%p) (k&1)?ans=(lint)ans*x%p:0;return ans; }
namespace POLY_space{
	const int N=200000;
	int A[N],B[N],C[N],r[N];
	inline void clr(int *a,int n) { memset(a,0,sizeof(int)*n); }
	inline void cpy(int *a,int *b,int n) { memcpy(a,b,sizeof(int)*n); }
	inline int NTT(int *a,int n,int s)
	{
		rep(i,1,n-1) if(i<r[i]) swap(a[i],a[r[i]]);
		for(int i=2;i<=n;i<<=1)
		{
			int wn=fast_pow(3,(s>0)?(p-1)/i:p-1-(p-1)/i);
			for(int j=0,t=i>>1,x,y;j<n;j+=i)
				for(int k=0,w=1;k<t;k++,w=(lint)w*wn%p)
					x=a[j+k],y=(lint)w*a[j+k+t]%p,
					a[j+k]=x+y,(a[j+k]>=p?a[j+k]-=p:0),
					a[j+k+t]=x-y,(a[j+k+t]<0?a[j+k+t]+=p:0);
		}
		if(s<0) for(int i=0,ninv=fast_pow(n,p-2);i<n;i++) a[i]=(lint)a[i]*ninv%p;
		return 0;
	}
	inline int tms(int *a,int m1,int *b,int m2,int *c)//a[m1]=b[m2]=0
	{
		int n=1,L=0;while(n<m1+m2-1) n<<=1,L++;
		rep(i,1,n-1) r[i]=(r[i>>1]>>1)|((i&1)<<(L-1));
		clr(A,n),cpy(A,a,m1),NTT(A,n,1);
		clr(B,n),cpy(B,b,m2),NTT(B,n,1);
		rep(i,0,n-1) A[i]=(lint)A[i]*B[i]%p;
		return NTT(A,n,-1),cpy(c,A,m1+m2-1),0;
	}
	inline int tms(int *a,int m1,int *b,int m2,int *c,int n) { return tms(a,m1,b,m2,C),cpy(c,C,n),0; }
	namespace POLYINV_space{
		int b[N],h[N];
		inline int polyinv(int *a,int m,int *res)//a[m]=0
		{
			int n=1;while(n<m) n<<=1;
			clr(b,n),b[0]=fast_pow(a[0],p-2);
			for(int i=2;i<=n;i<<=1)
			{
				cpy(h,b,i);
				rep(j,0,(i>>1)-1) h[j]+=h[j],(h[j]>=p?h[j]-=p:0);
				tms(b,i>>1,b,i>>1,b),tms(b,i,a,i,b,i);
				rep(j,0,i-1) b[j]=h[j]-b[j],(b[j]<0?b[j]+=p:0);
			}
			return cpy(res,b,m),0;
		}
	}using POLYINV_space::polyinv;
	inline int polydif(int *a,int n,int *b)
	{
		rep(i,1,n-1) b[i-1]=(lint)i*a[i]%p;return b[n-1]=0;
	}
	inline int polyint(int *a,int n,int *b)
	{
		rep(i,0,n-2) b[i+1]=(lint)a[i]*fast_pow(i+1,p-2)%p;return b[0]=0;
	}
	namespace POLYLN_space{
		int b[N],c[N];
		inline int polyln(int *a,int n,int *res) { return polyinv(a,n,b),polydif(a,n,c),tms(b,n,c,n,b),polyint(b,n,c),cpy(res,c,n),0; }
	}using POLYLN_space::polyln;
	
	namespace POLYEXP_space{
		int b[N],c[N],d[N];
		inline int polyexp(int *a,int m,int *res)
		{
			int n=1;while(n<(m<<1)) n<<=1;b[0]=1;
			clr(d,n),cpy(d,a,m);
			for(int i=2;i<=n;i<<=1)
			{
				polyln(b,i>>1,c);
				rep(j,0,(i>>1)-1) c[j]=d[j]-c[j],(c[j]<0?c[j]+=p:0);
				c[0]++,(c[0]>=p?c[0]-=p:0),tms(b,i>>1,c,i>>1,b);
			}
			return cpy(res,b,m),0;
		}
	}using POLYEXP_space::polyexp;
}using POLY_space::NTT;using POLY_space::polyinv;
using POLY_space::r;using POLY_space::clr;using POLY_space::cpy;
const int N=200000,MXK=17;
int fac[N],facinv[N],s[MXK][MXK];
int F[MXK][N],G[MXK][N],H[MXK][N],G0inv[N];
inline int getstl(int k)
{
	s[0][0]=1;
	rep(i,1,k) rep(j,1,i) s[i][j]=(s[i-1][j-1]+(lint)j*s[i-1][j])%p;
	return 0;
}
inline int C(int n,int m)
{
	if(n<0||m<0||n<m) return 0;int ans=1;
	rep(i,n-m+1,n) ans=(lint)ans*i%p;
	return (lint)ans*facinv[m]%p;
}
inline int prelude(int n)
{
	rep(i,fac[0]=1,n) fac[i]=(lint)i*fac[i-1]%p;
	facinv[n]=fast_pow(fac[n],p-2);
	for(int i=n-1;i>=0;i--) facinv[i]=(i+1ll)*facinv[i+1]%p;
	return 0;
}
int main()
{
	int n=inn(),k=inn(),g,ans=0;
	prelude(max(n,k)),getstl(k);
	rep(i,0,k) rep(j,0,n)
		g=(lint)C(C(j,2),i)*fast_pow(2,(j*(j-1ll)/2)%(p-1)-i)%p,
		G[i][j]=(lint)g*facinv[j]%p,(j?H[i][j]=(lint)g*facinv[j-1]%p:0);
	polyinv(G[0],n+1,G0inv);
	int z=1,L=0;while(z<=n*2) z<<=1,L++;
	rep(i,1,z-1) r[i]=(r[i>>1]>>1)|((i&1)<<(L-1));
	rep(i,0,k) NTT(H[i],z,1),NTT(G[i],z,1);NTT(G0inv,z,1);
	rep(i,0,k)
	{
		cpy(F[i],H[i],z);
		rep(j,0,i-1) rep(t,0,z-1) F[i][t]=(F[i][t]-(lint)F[j][t]*G[i-j][t])%p,(F[i][t]<0?F[i][t]+=p:0);
		NTT(F[i],z,-1);
		rep(j,n+1,z-1) F[i][j]=0;
		NTT(F[i],z,1);
		rep(j,0,z-1) F[i][j]=(lint)F[i][j]*G0inv[j]%p;
		NTT(F[i],z,-1);
		rep(j,n+1,z-1) F[i][j]=0;
		ans=(ans+(lint)F[i][n]*fac[n-1]%p*fac[i]%p*s[k][i])%p;
		NTT(F[i],z,1);
	}
	return !printf("%d\n",ans);
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值