【CF960G】Bandit Blues(第一类斯特林数,FFT)

【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;
}

猜你喜欢

转载自www.cnblogs.com/cjyyb/p/10145676.html