【BZOJ4555】[Tjoi2016&Heoi2016]求和 NTT

【BZOJ4555】[Tjoi2016&Heoi2016]求和

Description

在2016年,佳媛姐姐刚刚学习了第二类斯特林数,非常开心。

现在他想计算这样一个函数的值:
S(i, j)表示第二类斯特林数,递推公式为:
S(i, j) = j ∗ S(i − 1, j) + S(i − 1, j − 1), 1 <= j <= i − 1。
边界条件为:S(i, i) = 1(0 <= i), S(i, 0) = 0(1 <= i)
你能帮帮他吗?

Input

输入只有一个正整数

Output

 输出f(n)。由于结果会很大,输出f(n)对998244353(7 × 17 × 223 + 1)取模的结果即可。1 ≤ n ≤ 100000

Sample Input

3

Sample Output

87

题解:读书少,没见过第二类斯特林数,于是去百度找了定义及通项公式。

将n个不同的球放入m个无差别的盒子中,要求盒子非空,有几种方案?

由定义可知,原题中的j<=i可以变成j<=n,所以开始推式子啦!

 

然后右面是什么?卷积!直接NTT完成任务。

不要忘记讨论等比数列公比为1的情况!

#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
typedef long long ll;
const ll P=998244353;
const ll G=3;
const int maxn=(1<<19)+10;
int n,len;
ll ans;
ll A[maxn],B[maxn],ine[maxn],jcc[maxn],jc[maxn];
ll pm(ll x,ll y,ll z)
{
	ll ret=1;
	while(y)
	{
		if(y&1)	ret=ret*x%P;
		x=x*x%P,y>>=1;
	}
	return ret;
}
void NTT(ll *a,int f)
{
	int i,j,k,h;
	ll t;
	for(i=k=0;i<len;i++)
	{
		if(i>k)	swap(a[i],a[k]);
		for(j=len>>1;(k^=j)<j;j>>=1);
	}
	for(h=2;h<=len;h<<=1)
	{
		ll wn=pm(G,f==1?(P-1)/h:P-1-(P-1)/h,P);
		for(j=0;j<len;j+=h)
		{
			ll w=1;
			for(k=j;k<j+h/2;k++)	t=a[k+h/2]*w%P,a[k+h/2]=(a[k]-t+P)%P,a[k]=(a[k]+t)%P,w=w*wn%P;
		}
	}
	if(f==-1)
	{
		t=pm(len,P-2,P);
		for(i=0;i<len;i++)	a[i]=a[i]*t%P;
	}
}
int main()
{
	scanf("%d",&n);
	int i;
	for(len=1;len<=n+n;len<<=1);
	ine[1]=ine[0]=jcc[1]=jcc[0]=jc[1]=jc[0]=1;
	for(i=2;i<=n;i++)	ine[i]=(P-(P/i)*ine[P%i])%P,jcc[i]=jcc[i-1]*ine[i]%P,jc[i]=jc[i-1]*i%P;
	for(i=0;i<=n;i++)
	{
		A[i]=(((i&1)?-1:1)*jcc[i]+P)%P;
		if(i==1)	B[i]=(n+1)*jcc[i]%P;
		else	B[i]=(1-pm(i,n+1,P)+P)*pm(1-i,P-2,P)%P*jcc[i]%P;
	}
	NTT(A,1),NTT(B,1);
	for(i=0;i<len;i++)	A[i]=A[i]*B[i]%P;
	NTT(A,-1);
	for(i=0;i<=n;i++)	ans=(ans+jc[i]*pm(2,i,P)%P*A[i]%P)%P;
	printf("%lld",ans);
	return 0;
}
原文地址:https://www.cnblogs.com/CQzhangyu/p/7392112.html