[HEOI2016/TJOI2016]求和

一、题目

点此看题

二、解法

0x01 前置芝士

第二类斯特林数的模型是把 n n n个不同的小球放进 m m m个相同的盒子中,要求盒子非空的方案数,记作 S ( n , m ) S(n,m) S(n,m)

递推式: S ( n , m ) = S ( n − 1 , m − 1 ) + m ⋅ S ( n − 1 , m ) S(n,m)=S(n-1,m-1)+m\cdot S(n-1,m) S(n,m)=S(n1,m1)+mS(n1,m),也就是考虑加入一个球或一个盒子。

容斥推导的通项公式: S ( n , m ) = ∑ i = 0 m ( − 1 ) i × C ( m , i ) × ( m − i ) n S(n,m)=\sum_{i=0}^{m} (-1)^i\times C(m,i)\times (m-i)^n S(n,m)=i=0m(1)i×C(m,i)×(mi)n,也就是容斥非空的这个限制, i i i表示空出来的盒子个数,那么方案数就是 选出空出的盒子 × \times ×在剩下的盒子中随便放的方案数。

0x02 式子推导
f ( n ) = ∑ i = 0 n ∑ j = 0 i S ( i , j ) × 2 j × j ! = ∑ i = 0 n ∑ j = 0 i 2 j ∑ k = 0 j ( − 1 ) k × C ( j , k ) × ( j − k ) i = ∑ i = 0 n ∑ j = 0 i 2 j × j ! ∑ k = 0 j ( − 1 ) k k ! × ( j − k ) i ( j − k ) ! = ∑ i = 0 n ∑ j = 0 n 2 j × j ! ∑ k = 0 j ( − 1 ) k k ! × ( j − k ) i ( j − k ) ! = ∑ j = 0 n 2 j × j ! ∑ k = 0 j ( − 1 ) k k ! × ∑ i = 0 n ( j − k ) i ( j − k ) ! f(n)=\sum_{i=0}^{n}\sum_{j=0}^{i}S(i,j)\times 2^j\times j!\\=\sum_{i=0}^{n}\sum_{j=0}^i2^j\sum_{k=0}^{j}(-1)^k\times C(j,k)\times (j-k)^i\\=\sum_{i=0}^{n}\sum_{j=0}^i2^j\times j!\sum_{k=0}^{j}\frac{(-1)^k}{k!}\times\frac{(j-k)^i}{(j-k)!}\\=\sum_{i=0}^{n}\sum_{j=0}^n2^j\times j!\sum_{k=0}^{j}\frac{(-1)^k}{k!}\times\frac{(j-k)^i}{(j-k)!}\\=\sum_{j=0}^{n}2^j\times j!\sum_{k=0}^{j}\frac{(-1)^k}{k!}\times \frac{\sum_{i=0}^{n}(j-k)^i}{(j-k)!} f(n)=i=0nj=0iS(i,j)×2j×j!=i=0nj=0i2jk=0j(1)k×C(j,k)×(jk)i=i=0nj=0i2j×j!k=0jk!(1)k×(jk)!(jk)i=i=0nj=0n2j×j!k=0jk!(1)k×(jk)!(jk)i=j=0n2j×j!k=0jk!(1)k×(jk)i=0n(jk)i f ( i ) = ( − 1 ) k k ! , g ( i ) = ∑ j = 0 n i j i ! f(i)=\frac{(-1)^k}{k!},g(i)=\frac{\sum_{j=0}^n i^j}{i!} f(i)=k!(1)k,g(i)=i!j=0nij,则:
= ∑ j = 0 n 2 j × j ! ∑ k = 0 j f ( k ) g ( j − k ) =\sum_{j=0}^{n}2^j\times j!\sum_{k=0}^jf(k)g(j-k) =j=0n2j×j!k=0jf(k)g(jk)最后一个式子就是卷积的形式,用 NTT \text{NTT} NTT优化即可。
解释一下第四个柿子,因为我们的 S ( n , m ) S(n,m) S(n,m)(当 m > n m>n m>n时)用容斥公式算出来是 0 0 0,所以可以扩大 j j j的范围,方便改变枚举顺序, g g g数组需要用等比数列求和,我一开始写了一个预处理阶乘法。

#include <cstdio>
#include <iostream>
#include <cmath>
#define int long long
using namespace std;
const int MAXN = 300005;
const int MOD = 998244353;
int read()
{
    int num=0,flag=1;char c;
    while((c=getchar())<'0'||c>'9')if(c=='-')flag=-1;
    while(c>='0'&&c<='9')num=(num<<3)+(num<<1)+(c^48),c=getchar();
    return num*flag;
}
int n,len,lg,ans,f[MAXN],g[MAXN];
int Rev[MAXN],fac[MAXN],inv[MAXN];
void init(int n)
{
	inv[0]=inv[1]=fac[0]=fac[1]=1; 
	for(int i=2;i<=n;i++) inv[i]=inv[MOD%i]*(MOD-MOD/i)%MOD;
	for(int i=2;i<=n;i++) fac[i]=fac[i-1]*i%MOD;
	for(int i=2;i<=n;i++) inv[i]=inv[i]*inv[i-1]%MOD;
}
int qkpow(int a,int b)
{
	int res=1;
	while(b>0)
	{
		if(b&1) res=res*a%MOD;
		a=a*a%MOD;
		b>>=1;
	}
	return res;
}
void NTT(int *a,const int len,int tmp)
{
	for(int i=0;i<len;i++)
	{
		Rev[i]=(Rev[i>>1]>>1)|((i&1)<<(lg-1));
		if(i<Rev[i])
			swap(a[i],a[Rev[i]]);
	}
	for(int s=2;s<=len;s<<=1)
	{
		int t=s/2,w=(tmp==1)?qkpow(3,(MOD-1)/s):qkpow(3,(MOD-1)-(MOD-1)/s);
		for(int i=0;i<len;i+=s)
		{
			int x=1;
			for(int j=0;j<t;j++,x=x*w%MOD)
			{
				int fe=a[i+j],fo=a[i+j+t];
				a[i+j]=(fe+x*fo)%MOD;
				a[i+j+t]=((fe-fo*x)%MOD+MOD)%MOD;
			}
		}
	}
	if(tmp==1) return ;
	int inv=qkpow(len,MOD-2);
	for(int i=0;i<len;i++)
		a[i]=a[i]*inv%MOD;
}
signed main()
{
	n=read();
	init(n);
	for(int i=0;i<=n;i++)
		if(i%2) f[i]=MOD-inv[i];
		else f[i]=inv[i];
	g[0]=1;
	for(int i=1;i<=n;i++)
	{
		if(i==1) g[i]=n+1;
		else g[i]=(qkpow(i,n+1)-1)*qkpow(i-1,MOD-2)%MOD*inv[i]%MOD;
	}
	len=1;while(len<=2*n+2)len<<=1,lg++;
	NTT(f,len,1);NTT(g,len,1);
	for(int i=0;i<len;i++) f[i]=f[i]*g[i];
	NTT(f,len,-1);
	for(int i=0;i<=n;i++)
		ans+=qkpow(2,i)*fac[i]%MOD*f[i]%MOD,ans%=MOD;
	printf("%lld\n",(ans%MOD+MOD)%MOD);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值