$\newcommand{stirfirst}[2]{\left[\begin{matrix}#1\\#2\end{matrix}\right]}$题意:有一个$1\cdots N$的排列,从前往后贪心选取上升序列长度为$A$,从后往前长度为$B$,问有多少种可能的排列
首先对于每种满足要求的排列,我们按以下方式将$1\cdots N-1$分成$A+B-2$组:
对于$N$的左边,每个被选取的数一直到(下一个被选取的数的前一位)分为一组,右边类似
假设某组有$k$个数,那么除了最大的那个数,其他数可以随意排列,有$(k-1)!$种方案,注意到$k$个数排成一个轮换的方案数也是$(k-1)!$($\stirfirst k1=(k-1)!$),所以我们可以将这种分组看做把$N-1$个元素划分为$A+B-2$个轮换,方案数为$\stirfirst{N-1}{A+B-2}$
以上并没有考虑$N$,我们分好组后,还要决定把哪些组排在$N$的左边,方案数为$\begin{align*}\binom{A+B-2}{A-1}\end{align*}$
总方案乘起来就好了,$N\leq10^5$所以要用分治FFT算第一类斯特林数
p.s.这是个HDU原题(只不过原题是$O(n^2)$预处理,$O(1)$回答多组询问)
#include<stdio.h>
#include<string.h>
typedef long long ll;
const int mod=998244353;
int mul(int a,int b){return a*(ll)b%mod;}
int ad(int a,int b){return(a+b)%mod;}
int de(int a,int b){return(a-b)%mod;}
int pow(int a,int b){
int s=1;
while(b){
if(b&1)s=mul(s,a);
a=mul(a,a);
b>>=1;
}
return s;
}
int rev[262144],N,iN;
void pre(int n){
int i,k;
for(N=1,k=0;N<n;N<<=1)k++;
for(i=0;i<N;i++)rev[i]=(rev[i>>1]>>1)|((i&1)<<(k-1));
iN=pow(N,mod-2);
}
void swap(int&a,int&b){a^=b^=a^=b;}
void ntt(int*a,int on){
int i,j,k,t,w,wn;
for(i=0;i<N;i++){
if(i<rev[i])swap(a[i],a[rev[i]]);
}
for(i=2;i<=N;i<<=1){
wn=pow(3,(on==1)?(mod-1)/i:(mod-1-(mod-1)/i));
for(j=0;j<N;j+=i){
w=1;
for(k=0;k<i>>1;k++){
t=mul(w,a[i/2+j+k]);
a[i/2+j+k]=de(a[j+k],t);
a[j+k]=ad(a[j+k],t);
w=mul(w,wn);
}
}
}
if(on==-1){
for(i=0;i<N;i++)a[i]=mul(a[i],iN);
}
}
void stir(int*a,int l,int r){
if(l==r){
a[1]=1;
a[0]=l;
return;
}
int mid,i,*b,*c;
mid=(l+r)>>1;
for(i=1;i<r-l+3;i<<=1);
b=new int[i];
memset(b,0,i<<2);
c=new int[i];
memset(c,0,i<<2);
stir(b,l,mid);
stir(c,mid+1,r);
pre(r-l+3);
ntt(b,1);
ntt(c,1);
for(i=0;i<N;i++)a[i]=mul(b[i],c[i]);
ntt(a,-1);
for(i=r-l+2;i<N;i++)a[i]=0;
}
int s[262144],fac[200010],rfac[200010];
int C(int n,int k){return mul(fac[n],mul(rfac[k],rfac[n-k]));}
int main(){
int n,a,b,i;
scanf("%d%d%d",&n,&a,&b);
if(n==1){
putchar((a==1&&b==1)?'1':'0');
return 0;
}
if(a+b-2>n-1||a==0||b==0){
putchar('0');
return 0;
}
stir(s,0,n-2);
n<<=1;
fac[0]=1;
for(i=1;i<=n;i++)fac[i]=mul(fac[i-1],i);
rfac[n]=pow(fac[n],mod-2);
for(i=n;i>0;i--)rfac[i-1]=mul(rfac[i],i);
printf("%d",(mul(C(a+b-2,a-1),s[a+b-2])+mod)%mod);
}
2018.4.20补了$O(n\log_2n)$的做法,比分治FFT快许多(1325ms VS 390ms)
#include<stdio.h>
#include<string.h>
typedef long long ll;
const int mod=998244353;
int mul(int a,int b){return a*(ll)b%mod;}
int ad(int a,int b){return(a+b)%mod;}
int de(int a,int b){return(a-b)%mod;}
int pow(int a,int b){
int s=1;
while(b){
if(b&1)s=mul(s,a);
a=mul(a,a);
b>>=1;
}
return s;
}
int rev[262144],N,iN;
void pre(int n){
int i,k;
for(N=1,k=0;N<n;N<<=1)k++;
for(i=0;i<N;i++)rev[i]=(rev[i>>1]>>1)|((i&1)<<(k-1));
iN=pow(N,mod-2);
}
void swap(int&a,int&b){a^=b^=a^=b;}
void ntt(int*a,int on){
int i,j,k,t,w,wn;
for(i=0;i<N;i++){
if(i<rev[i])swap(a[i],a[rev[i]]);
}
for(i=2;i<=N;i<<=1){
wn=pow(3,(on==1)?(mod-1)/i:(mod-1-(mod-1)/i));
for(j=0;j<N;j+=i){
w=1;
for(k=0;k<i>>1;k++){
t=mul(w,a[i/2+j+k]);
a[i/2+j+k]=de(a[j+k],t);
a[j+k]=ad(a[j+k],t);
w=mul(w,wn);
}
}
}
if(on==-1){
for(i=0;i<N;i++)a[i]=mul(a[i],iN);
}
}
int fac[262144],rfac[262144],a[262144],b[262144];
void stir(int*s,int n){
if(n==1){
s[0]=0;
s[1]=1;
return;
}
int i,m;
m=n>>1;
stir(s,m);
pre(n+1);
memset(a,0,N<<2);
memset(b,0,N<<2);
for(i=0;i<=m;i++){
a[m-i]=mul(s[i],fac[i]);
b[i]=mul(pow(m,i),rfac[i]);
}
ntt(a,1);
ntt(b,1);
for(i=0;i<N;i++)a[i]=mul(a[i],b[i]);
ntt(a,-1);
memset(b,0,N<<2);
for(i=0;i<=m;i++)b[i]=mul(a[m-i],rfac[i]);
ntt(s,1);
ntt(b,1);
for(i=0;i<N;i++)s[i]=mul(s[i],b[i]);
ntt(s,-1);
for(i=m<<1|1;i<N;i++)s[i]=0;
if(n&1){
for(i=n-1;i>=0;i--)s[i+1]=ad(s[i],mul(n-1,s[i+1]));
}
}
int s[262144];
int C(int n,int k){return mul(fac[n],mul(rfac[k],rfac[n-k]));}
int main(){
int n,i,a,b;
scanf("%d%d%d",&n,&a,&b);
if(n==1){
putchar((a==1&&b==1)?'1':'0');
return 0;
}
if(a+b-2>n-1||a==0||b==0){
putchar('0');
return 0;
}
fac[0]=1;
for(i=1;i<=n;i++)fac[i]=mul(fac[i-1],i);
rfac[n]=pow(fac[n],mod-2);
for(i=n;i>0;i--)rfac[i-1]=mul(rfac[i],i);
stir(s,n-1);
printf("%d",(mul(s[a+b-2],C(a+b-2,a-1))+mod)%mod);
}