BZOJ 5093: 图的价值 第二类斯特林数$O(n \log n)$

版权声明:本文为博主原创文章,未经博主允许必须转载。 https://blog.csdn.net/qq_35950004/article/details/86314161

简单题意

一个带标号的图的价值定义为每个点度数的 k ( < = 2 e 5 ) k(<=2e5) 次方的和。
n ( < = 1 e 9 ) n(<=1e9) 个点的带标号的简单无向图的价值之和 ( m o d 998244353 ) \pmod {998244353}

题解

每个点只有标号之别,故只需要求出一个点的价值之和 × n \times n 即可。
a n s = n 2 C ( n 1 , 2 ) i = 0 n 1 C ( n 1 , i ) i k i = 0 n 1 C ( n 1 , i ) i k = i = 0 n 1 C ( n 1 , i ) j = 0 k s t r 2 ( k , j ) j ! C ( i , j ) = j = 0 k s t r 2 ( k , j ) j ! i = j n 1 C ( n 1 , i ) C ( i , j ) = j = 0 k s t r 2 ( k , j ) j ! C ( n 1 , j ) i = 0 n 1 j C ( n 1 j , i ) = j = 0 k s t r 2 ( k , j ) j ! C ( n 1 , j ) 2 n 1 j \begin{aligned} &ans =n * 2^{C(n-1,2)}*\sum_{i=0}^{n-1} C(n-1,i) * i^k\\ &\sum_{i=0}^{n-1} C(n-1,i) * i^k = \sum_{i=0}^{n-1} C(n-1,i) * \sum_{j=0}^{k} str2(k,j) * j! * C(i,j)\\ &=\sum_{j=0}^{k}str2(k,j) * j! * \sum_{i=j}^{n-1}C(n-1,i) * C(i,j)\\ &=\sum_{j=0}^{k}str2(k,j) * j! * C(n-1,j) * \sum_{i=0}^{n-1-j} C(n-1-j,i)\\ &=\sum_{j=0}^{k}str2(k,j) * j! * C(n-1,j) * 2^{n-1-j} \end{aligned}
计算第二类斯特林数即可。
容斥原理可得:
s t r 2 ( n , k ) = 1 k ! i = 0 k C ( k , i ) ( k i ) n ( 1 ) i = i = 0 k ( 1 ) i i ! ( k i ) n ( k i ) ! str2(n,k) = \frac 1{k!}\sum_{i=0}^k C(k,i) * (k-i)^n*(-1)^i = \sum_{i=0}^k\frac {(-1)^i}{i!}*\frac {(k-i)^n}{(k-i)!}
化为EGF就行了。
F F T   O ( n log n ) FFT \ O(n \log n)

AC Code:

#include<cstdio>
#include<cstring>
#include<cctype>
#include<algorithm>
#define maxn 600005
#define mod 998244353
#define LL long long
using namespace std;

int n,k;
const int inv2 = (mod + 1) / 2;
int w[maxn]={1},r[maxn],lg[maxn],wlen,inv[maxn]={1,1},fac[maxn]={1,1},invf[maxn]={1,1};
int Pow(int base,LL k)
{
	int ret = 1;
	for(;k;k>>=1,base=1ll*base*base%mod) 
		if(k&1) 
			ret = 1ll * ret * base % mod;
	return ret;
}
void Init(int n)
{
	for(wlen=1;n>=2*wlen;wlen<<=1);
	for(int i=1,pw=Pow(3,(mod-1)/(2*wlen));i<=2*wlen;i++) w[i] = 1ll * w[i-1] * pw % mod;
	for(int i=2;i<=2*wlen;i++)	
		lg[i] = lg[i>>1] + 1,
		fac[i] = 1ll * fac[i-1] * i % mod,
		inv[i] = 1ll * (mod - mod / i) * inv[mod % i] % mod,
		invf[i] = 1ll * invf[i-1] * inv[i] %mod;
}
inline void NTT(int *A,int n,int tp)
{
	int lgn = lg[n];
	for(int i=1;i<n;i++) r[i] = (r[i>>1]>>1)|((i&1)<<(lgn-1));
	for(int i=1;i<n;i++) if(i<r[i]) swap(A[i],A[r[i]]);
	for(int L=2;L<=n;L<<=1)
		for(int st=0,l=L>>1,inc=wlen/l;st<n;st+=L)
			for(int k=st,x=0;k<st+l;k++,x+=inc)
			{
				int tmp = 1ll * (tp == 1 ? w[x] : w[2*wlen-x]) * A[k+l] % mod;
				A[k+l] = (A[k] - tmp) % mod , A[k] = (A[k] + tmp) % mod;
			}
	if(tp == -1)
		for(int i=0,inv=Pow(n,mod-2);i<n;i++)
			A[i] = 1ll * A[i]  * inv % mod;
}
inline int C(int a,int b){ return 1ll * fac[a] * invf[b] % mod * invf[a-b] % mod; }
int a[maxn],b[maxn],str[maxn];
int main()
{
	scanf("%d%d",&n,&k);
	Init(2*k);
	for(int i=0;i<=k;i++) a[i] = (i&1?-1:1) * invf[i] , b[i] = 1ll * Pow(i,k) * invf[i] % mod;
	int len = 1;
	for(;2*k>=len;len<<=1);
	NTT(a,len,1),NTT(b,len,1);
	for(int i=0;i<len;i++) str[i] = 1ll * a[i] * b[i] % mod;
	NTT(str,len,-1);
	int ans = n * 1ll * Pow(2,1ll*(n-1)*(n-2)/2) % mod , sum = 0;
	for(int i=0,C=1,pw=Pow(2,n-1);i<=k;pw=1ll*pw*inv2%mod,C=1ll*C*(n-1-i)%mod*inv[i+1]%mod,i++)
		sum = (sum + 1ll * str[i] * fac[i] % mod * C % mod * pw % mod) % mod;
	printf("%lld\n",(1ll*sum*ans%mod+mod)%mod);
}

猜你喜欢

转载自blog.csdn.net/qq_35950004/article/details/86314161