求和
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
多项式求逆……
真是丧心病狂啊……
思路:
直接拆式子貌似不太好拆。
于是考虑换一种方法表示原问题:
令
g(n)=∑i=1n{ni}∗2i∗i!
,求其前缀和。
考虑
g(n)
的组合意义,代表将
n
个
所以可以把式子写成下面这样:
即枚举第一个集合的方案数乘以剩余集合的方案数。
考虑向卷积靠近:
指数型生成函数!
于是构造如下生成函数:
那么有如下式子:
G(x)=F(x)∗G(x)+1
注意这里的
1
实际上指的是,由于
然后有
然后构造出 F(x) 并多项式求逆即可得到 G(x) 的系数,求和得到答案~
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
using namespace std;
typedef long long ll;
const int N=400009;
const ll md=998244353;
int n,m;
ll G[N],F[N],C[N];
ll fac[N],inv[N];
int rev[N];
inline ll qpow(ll a,ll b)
{
ll ret=1;
while(b)
{
if(b&1)
ret=ret*a%md;
a=a*a%md;
b>>=1;
}
return ret;
}
inline void NTT(ll *a,int n,int f)
{
for(int i=0;i<n;i++)if(i<rev[i])swap(a[i],a[rev[i]]);
for(int h=2;h<=n;h<<=1)
{
ll w=qpow(3,(md-1)/h);
if(f)w=qpow(w,md-2);
for(int j=0;j<n;j+=h)
{
ll wn=1ll;
for(int k=j;k<j+(h>>1);k++)
{
ll x=a[k],y=wn*a[k+(h>>1)]%md;
a[k]=(x+y)%md;
a[k+(h>>1)]=(x-y+md)%md;
wn=wn*w%md;
}
}
}
if(f)
for(ll i=0,invs=qpow(n,md-2);i<n;i++)
a[i]=a[i]*invs%md;
}
inline void cinv(ll *a,ll *b,int n)
{
if(n==0)
{
b[0]=qpow(a[0],md-2);
return;
}
cinv(a,b,n>>1);n<<=1;
for(int i=0;i<n;i++)
rev[i]=(rev[i>>1]>>1)|((i&1)*(n>>1));
for(int i=0;i<n/2;i++)
C[i]=a[i],C[i+n/2]=0;
NTT(C,n,0);NTT(b,n,0);
for(int i=0;i<n;i++)
b[i]=(2*b[i]%md-C[i]*b[i]%md*b[i]%md+md)%md;
NTT(b,n,1);
for(int i=n/2;i<n;i++)b[i]=0;
}
int main()
{
scanf("%d",&m);
for(n=1;n<=m;n<<=1);
fac[0]=1;
for(ll i=1;i<=n;i++)
fac[i]=fac[i-1]*i%md;
inv[n]=qpow(fac[n],md-2);
for(ll i=n;i>=1;i--)
inv[i-1]=inv[i]*i%md;
for(int i=1;i<=m;i++)
F[i]=(md-inv[i])*2%md;
F[0]=1;
cinv(F,G,n);
ll ans=0;
for(int i=m;i>=0;i--)
(ans+=G[i]*fac[i]%md)%=md;
printf("%lld\n",ans);
return 0;
}