第一类斯特林数
暑假里,小L到海边去玩,捡了
个不同的贝壳。现在她想把这些贝壳串成
个项链(项链是环形的)。她忽然很疑惑,这有多少种方案呢?
聪明的小L很快想到,假设
为
个贝壳串成
条项链的方案数,那么显然有
,即要么将第
个贝壳单独串成一条项链,要么让前
个贝壳已经串成
条项链,然后考虑第
个贝壳放到哪个贝壳的后面。
这个递推式还可以看作进行
次操作,其中第
次操作有
种方案取一个新物品,有
种方案不取物品,最后取得
个物品的方案数。根据这个意义,小L写出了一个生成函数:
这个生成函数的 次项系数就是 。可以用分治FFT做到 的复杂度。
通过查阅资料,小L还得知, 这个多项式的每一项系数就是有符号第一类斯特林数,它们的递推式为
例题:codeforces960G
设
表示
个数的排列,存在
个数,在它们前面没有比它们大的数。
考虑最小的数放在哪,可以得到递推式:
,就是第一类斯特林数。
因为
的前面和后面都没有比它更大的数,所以题目要求的
个数一定在
前面,
个数一定在
后面,枚举
所在的位置,答案就是:
考虑一些前面无比其大数的数,假设它们所在的位置为 ,把 看作是一整块,那么我们可以先产生 块,然后从其中选择 块,将它们块内翻转,然后丢到 的后面。所以答案就是:
#include<bits/stdc++.h>
using namespace std;
#define RI register int
const int N=200005,mod=998244353,G=3;
int n,A,B,ans,a[18][N],rev[N];
int ksm(int x,int y) {
int re=1;
for(RI i=y;i;i>>=1,x=1LL*x*x%mod) if(i&1) re=1LL*re*x%mod;
return re;
}
void NTT(int *a,int n,int x) {
for(RI i=0;i<n;++i) if(rev[i]>i) swap(a[i],a[rev[i]]);
for(RI i=1;i<n;i<<=1) {
int gn=ksm(G,(mod-1)/(i<<1));
for(RI j=0;j<n;j+=(i<<1)) {
int g=1,t1,t2;
for(RI k=0;k<i;++k,g=1LL*g*gn%mod) {
t1=a[j+k],t2=1LL*g*a[j+i+k]%mod;
a[j+k]=(t1+t2)%mod,a[j+i+k]=(t1-t2+mod)%mod;
}
}
}
if(x==1) return;
int inv=ksm(n,mod-2);reverse(a+1,a+n);//a+1!!!
for(RI i=0;i<n;++i) a[i]=1LL*a[i]*inv%mod;
}
void work(int s,int t,int d) {
if(s==t) {a[d][0]=s,a[d][1]=1;return;}
int mid=(s+t)>>1,len=0,kn=1;
work(s,mid,d+1);
for(RI i=0;i<=mid-s+1;++i) a[d][i]=a[d+1][i];
work(mid+1,t,d+1);
while(kn<=t-s+1) kn<<=1,++len;
for(RI i=0;i<kn;++i) rev[i]=(rev[i>>1]>>1)|((i&1)<<(len-1));
for(RI i=mid-s+2;i<kn;++i) a[d][i]=0;
for(RI i=t-mid+1;i<kn;++i) a[d+1][i]=0;
NTT(a[d],kn,1),NTT(a[d+1],kn,1);
for(RI i=0;i<kn;++i) a[d][i]=1LL*a[d][i]*a[d+1][i]%mod;
NTT(a[d],kn,-1);
}
int C(int d,int u) {
int k1=1,k2=1;
for(RI i=d-u+1;i<=d;++i) k1=1LL*k1*i%mod;
for(RI i=1;i<=u;++i) k2=1LL*k2*i%mod;
return 1LL*k1*ksm(k2,mod-2)%mod;
}
int main()
{
scanf("%d%d%d",&n,&A,&B);
if(!A||!B||A+B-2>n-1) {puts("0");return 0;}
if(n==1) {puts("1");return 0;}
work(0,n-2,0);
ans=1LL*a[0][A+B-2]*C(A+B-2,B-1)%mod;
printf("%d\n",ans);
return 0;
}
第二类斯特林数
现在,小L准备把这些项链送给她的朋友们。假设有
条项链,项链各不相同。她准备了
个一模一样的礼盒,她准备将项链装进礼盒里且不让任何礼盒是空的。现在她又想知道有多少种方案了。
通过考虑第
条项链是单独放一个礼盒还是跟其他项链挤同一个礼盒,小L很快写出了递推式:
虽然不能生成函数做了,但是可以利用容斥原理,考虑空几个盒和盒子无序,得到一个新式子:
这个可以写成卷积形式,用FFT 求出。
例题:log2058/洛谷P4091
原式=
将第二类斯特林数的通项公式代入得:
发现后面的那个东西很像卷积,于是我们就让它更像卷积一点:
对了, ,所以函数 和 都还挺好求的,那么答案就是:
#include<bits/stdc++.h>
using namespace std;
#define RI register int
const int mod=998244353,G=3,N=262150;
int n,kn,len,ans;
int a[N],b[N],fac[N],ni[N],rev[N];
int ksm(int x,int y) {
int re=1;
for(;y;y>>=1,x=1LL*x*x%mod) if(y&1) re=1LL*re*x%mod;
return re;
}
void NTT(int *a,int n,int x) {
for(RI i=0;i<n;++i) if(rev[i]>i) swap(a[i],a[rev[i]]);
for(RI i=1;i<n;i<<=1) {
int gn=ksm(G,(mod-1)/(i<<1));
for(RI j=0;j<n;j+=(i<<1)) {
int g=1,t1,t2;
for(RI k=0;k<i;++k,g=1LL*g*gn%mod) {
t1=a[j+k],t2=1LL*g*a[j+i+k]%mod;
a[j+k]=(t1+t2)%mod,a[j+i+k]=(t1-t2+mod)%mod;
}
}
}
if(x==1) return;
int inv=ksm(n,mod-2);reverse(a+1,a+n);
for(RI i=0;i<n;++i) a[i]=1LL*a[i]*inv%mod;
}
int main()
{
scanf("%d",&n);
fac[0]=1;for(RI i=1;i<=n;++i) fac[i]=1LL*fac[i-1]*i%mod;
ni[n]=ksm(fac[n],mod-2);
for(RI i=n-1;i>=0;--i) ni[i]=1LL*ni[i+1]*(i+1)%mod;
for(RI i=0;i<=n;++i) {
a[i]=1LL*(1-2*(i&1)+mod)%mod*ni[i]%mod;
if(i!=1) b[i]=1LL*(ksm(i,n+1)-1+mod)%mod*ni[i]%mod*ksm(i-1+mod,mod-2)%mod;
else b[i]=n+1;
}
kn=1;while(kn<=n+n) kn<<=1,++len;
for(RI i=0;i<kn;++i) rev[i]=(rev[i>>1]>>1)|((i&1)<<(len-1));
NTT(a,kn,1),NTT(b,kn,1);
for(RI i=0;i<kn;++i) a[i]=1LL*a[i]*b[i]%mod;
NTT(a,kn,-1);
for(RI i=0,j=1;i<=n;++i,j=(j+j)%mod)
ans=(ans+1LL*j*fac[i]%mod*a[i]%mod)%mod;
printf("%d\n",ans);
return 0;
}