【CF960G】Bandit Blues(第一類斯特林數,FFT)
題面
洛谷
CF
求前綴最大值有\(a\)個,后綴最大值有\(b\)個的長度為\(n\)的排列個數。
題解
完完全全就是【FJOI】建築師的加強版本。
顯然每一個前綴最大值和一段連續的區間構成了一個環排列,顯然每個前綴最大值就是這個環中的最大值。而全局最大值一定把前后綴最大值分開。
所以答案考慮除最大值外,左側需要\(a-1\)個前綴最大值,右側需要\(b-1\)個前綴最大值。也就是一共要\(a+b-2\)個環,那么這一部分的貢獻是\(\begin{bmatrix}n-1\\a+b-2\end{bmatrix}\)。而環在左右隨意分配,所以再乘上一個\(a+b-2\choose a-1\)。
解釋一個小問題,為什么不需要考慮環的最大值的大小關系,因為我們強制在排列過程中按照最大值從小往大放,而每個環因為放置的時候是一個線段,那么我們保證最大值一定在靠外側,這樣子后面的比它小的值必定不是前綴或者后綴最大值。
那么問題轉化成了怎么預處理第一類斯特林數。戳這里
#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
#define MOD 998244353
#define MAX 300000
inline int read()
{
int x=0;bool t=false;char ch=getchar();
while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
if(ch=='-')t=true,ch=getchar();
while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
return t?-x:x;
}
int fpow(int a,int b)
{
int s=1;
while(b){if(b&1)s=1ll*s*a%MOD;a=1ll*a*a%MOD;b>>=1;}
return s;
}
int r[MAX],W[MAX];
void NTT(int *P,int opt,int N)
{
int l=0;for(int i=1;i<N;i<<=1)++l;
for(int i=0;i<N;++i)r[i]=(r[i>>1]>>1)|((i&1)<<(l-1));
for(int i=0;i<N;++i)if(i<r[i])swap(P[i],P[r[i]]);
for(int i=1;i<N;i<<=1)
{
int w=fpow(3,(MOD-1)/(i<<1));W[0]=1;
for(int k=1;k<i;++k)W[k]=1ll*W[k-1]*w%MOD;
for(int p=i<<1,j=0;j<N;j+=p)
for(int k=0;k<i;++k)
{
int X=P[j+k],Y=1ll*W[k]*P[i+j+k]%MOD;
P[j+k]=(X+Y)%MOD;P[i+j+k]=(X+MOD-Y)%MOD;
}
}
if(opt==-1)
{
reverse(&P[1],&P[N]);
for(int i=0,inv=fpow(N,MOD-2);i<N;++i)P[i]=1ll*P[i]*inv%MOD;
}
}
int S[MAX],jc[MAX],jv[MAX],inv[MAX];
int C(int n,int m){if(n<m)return 0;return 1ll*jc[n]*jv[m]%MOD*jv[n-m]%MOD;}
int A[MAX],B[MAX],pw[MAX];
void Solve(int len)
{
if(len==0){S[0]=1;return;}
if(len==1){S[1]=1;return;}
if(len&1)
{
Solve(len-1);
for(int i=len;i;--i)S[i]=(S[i-1]+1ll*S[i]*(len-1))%MOD;
}
else
{
Solve(len>>1);int l=len>>1,N;
for(N=1;N<=len;N<<=1);
pw[0]=1;for(int i=1;i<=l;++i)pw[i]=1ll*pw[i-1]*l%MOD;
for(int i=0;i<=l;++i)A[i]=1ll*S[i]*jc[i]%MOD;
for(int i=0;i<=l;++i)B[i]=1ll*pw[i]*jv[i]%MOD;
reverse(&B[0],&B[l+1]);
NTT(A,1,N);NTT(B,1,N);
for(int i=0;i<N;++i)A[i]=1ll*A[i]*B[i]%MOD;
NTT(A,-1,N);
for(int i=0;i<=l;++i)A[i]=1ll*A[i+l]*jv[i]%MOD;
for(int i=l+1;i<N;++i)A[i]=B[i]=0;
for(int i=0;i<=l;++i)B[i]=S[i];
NTT(A,1,N);NTT(B,1,N);
for(int i=0;i<N;++i)A[i]=1ll*A[i]*B[i]%MOD;
NTT(A,-1,N);
for(int i=0;i<=len;++i)S[i]=A[i];
for(int i=0;i<N;++i)A[i]=B[i]=0;
}
}
int n,a,b;
int main()
{
n=read();a=read();b=read();
jc[0]=jv[0]=inv[0]=inv[1]=1;
for(int i=1;i<=max(a+b,n);++i)jc[i]=1ll*jc[i-1]*i%MOD;
for(int i=2;i<=max(a+b,n);++i)inv[i]=1ll*inv[MOD%i]*(MOD-MOD/i)%MOD;
for(int i=1;i<=max(a+b,n);++i)jv[i]=1ll*jv[i-1]*inv[i]%MOD;
Solve(n-1);int ans=1ll*C(a+b-2,a-1)*S[a+b-2]%MOD;
printf("%d\n",ans);
return 0;
}