• CF960G Bandit Blues


    Description

    题目链接

    计算 (1...n) 的全排列中不同的前缀最大值的个数等于 (a) 且不同的后缀最大值的个数等于 (b) 的排列的数目对 (998244353) 取模的结果

    (1le nle 10^5,0le a,ble n​)

    Solution

    画一画合法的排列可以分析得出以下信息

    • 无论是前缀最大值还是后缀最大值,新出现的最大值 (k​) 总是会以 ({k,i_1,i_2,...,i_c}​) 的形式出现,其中 (i_1,i_2,...,i_c​) 为任意 (c​) 个小于 (k​) 的数,(c​) 可以为 (0​)

    • 整个排列的最大值,即 (n​),会将这个序列分为前后两个排列,而这两个排列是独立的

    • 由于我们只关注排列的相对大小关系,所以被处在下标 (i​) 位置的 (n​) 划分出的两个小排列可以直接看作 ({1,2,...,i-1}​)({1,2,...,n-i}​) 两组元素的排列

    所以我们只需要求 (1...i) 的全排列中不同的前缀最大值的个数等于 (s) 的排列个数即可,(iin [0,n],sin[1,n])

    从第一条性质我们可以看出,如果我们将 (n) 个元素划分为 (j) 个集合,并默认其中的最大值为 (k) ,并按每个集合的 (k) 将这 (j) 个集合从小到大排序,那么这一定是一种符合条件的方案,并且这种构造方法可以包括所有的合法方案

    但是如果这样用第二类斯特林数做的话,对于每一个大小为 (s) 的集合,我们还需乘上 ((s-1)!),这样就不太好做了

    实际上第一类斯特林数可以很好地解决这个问题,因为它本质上是枚举将长度为 (n) 的序列分解为 (i) 个循环的方案数,符合我们的要求

    所以我们最终要求的是 (egin{bmatrix}i\a-1end{bmatrix})(egin{bmatrix}i\b-1end{bmatrix}),其中 (iin [0,n-1])

    答案就是 ( ext{ans}=sumlimits_{i=1}^{n}egin{bmatrix}i-1\a-1end{bmatrix}egin{bmatrix}n-i\b-1end{bmatrix}​)

    那么怎么快速求第一类斯特林数呢?

    我们知道 (egin{bmatrix}n\iend{bmatrix}) 的生成函数是 (x^{overline{n}}),但这只能快速求一行的第一类斯特林数,对于求一列的话,复杂度就退化成 (O(n^2log n))

    然后发现没有快速求一列第一类斯特林数的方法。。。(其实是我不会)

    从另一个角度思考问题,不如忽略掉第三条性质,直接生成 (a+b-2) 个循环再分配到两边,这样也是对的

    那么答案就是 ( ext{ans}=egin{bmatrix}n-1\a+b-2end{bmatrix}dbinom{a+b-2}{a-1})

    组合数很好求,下面具体说说如何求第一类斯特林数

    因为 (egin{bmatrix}n\iend{bmatrix}=[x^i](x(x+1)(x+2)...(x+n-1))) ,所以关键是如何求这 (n) 个多项式的卷积

    考虑倍增

    假设现在已经求出了 (x(x+1)(x+2)...(x+k-1)) ,需要求 (x(x+1)(x+2)...(x+k-1)(x+k)(x+k+1)...(x+2k-1))

    显然后一半的式子可以通过将 (x+k) 带入前一半的式子得到,然后再把两个式子 ( ext{NTT}) 一下就可以得到我们想要的式子了

    怎么带入呢?考虑二项式定理,那么对于 (p) 次项,它的系数为

    [sumlimits_{i=p}^{k}a_idbinom{i}{p}k^{i-p} ag{1} ]

    其中 (a_i) 为带入前第 (i) 次项的系数

    展开这个式子,得到

    [frac{1}{p!}sumlimits_{i=p}^{k}a_ii!frac{k^{i-p}}{(i-p)!} ag{2} ]

    定义 (f_i=a_ii!,g_i=frac{k^i}{i!}),那么带入后第 (p) 次项系数 (a'_p)

    [a'_p=frac{1}{p!}sumlimits_{i=p}^{k}f_ig_{i-p} ag{3} ]

    (g_i) 中的值全部翻转,得 (g'_i),那么

    [a'_p=frac{1}{p!}sumlimits_{i=0}^{k-p}f_{k-i}g'_{p+i} ag{4} ]

    也可以写成

    [a'_p=frac{1}{p!}sumlimits_{i=0}^{k+p}f_ig'_{k+p-i} ag{5} ]

    然后就能 ( ext{NTT}​) 求出系数了

    复杂度 (O(nlog n))

    代码如下:

    #include<cstdio>
    #include<iostream>
    #include<algorithm>
    using namespace std;
    const int N=1e5+10;
    const int mod=998244353;
    const int G=3;
    const int invG=332748118;
    int n,A,B,fac[N<<1],inv[N<<1],f[N<<2],g[N<<2],a[N<<2],b[N<<2],k,now,INV;
    inline void Preprocess(){
    	fac[0]=1;for(register int i=1;i<=(n<<1);i++)fac[i]=1ll*fac[i-1]*i%mod;
    	inv[0]=inv[1]=1;for(register int i=2;i<=(n<<1);i++)inv[i]=(-1ll*mod/i*inv[mod%i]%mod+mod)%mod;
    	for(register int i=2;i<=(n<<1);i++)inv[i]=1ll*inv[i-1]*inv[i]%mod;
    }
    inline int C(int n,int m){if(n<0||m<0||n<m)return 0;return 1ll*fac[n]*inv[m]%mod*inv[n-m]%mod;}
    inline int fas(int x,int p){int res=1;while(p){if(p&1)res=1ll*res*x%mod;p>>=1;x=1ll*x*x%mod;}return res;}
    inline int MOD(int x){x-=x>=mod? mod:0;return x;}
    inline void NTT(int *a,int f){
    	for(register int i=0,j=0;i<k;i++){
    		if(i>j)swap(a[i],a[j]);
    		for(register int l=k>>1;(j^=l)<l;l>>=1);}
    	for(register int i=1;i<k;i<<=1){
    		int w=fas(~f? G:invG,(mod-1)/(i<<1));
    		for(register int j=0;j<k;j+=(i<<1)){
    			int e=1;
    			for(register int p=0;p<i;p++,e=1ll*e*w%mod){
    				int x=a[j+p],y=1ll*a[j+p+i]*e%mod;
    				a[j+p]=MOD(x+y);a[j+p+i]=MOD(x-y+mod);
    			}
    		}
    	}
    }
    inline void Solve(int m){
    	if(m==1){f[1]=1;return;}
    	int M=m>>1;Solve(M);
    	for(register int i=0;i<=M;i++)a[i]=1ll*f[i]*fac[i]%mod;
    	now=1;
    	for(register int i=0;i<=M;i++)
    		b[i]=1ll*now*inv[i]%mod,now=1ll*now*M%mod;
    	reverse(b,b+M+1);
    	k=1;while(k<=M+M)k<<=1;INV=fas(k,mod-2);
    	for(register int i=M+1;i<k;i++)a[i]=b[i]=0;
    	NTT(a,1);NTT(b,1);
    	for(register int i=0;i<k;i++)a[i]=1ll*a[i]*b[i]%mod;
    	NTT(a,-1);
    	for(register int i=0;i<k;i++)a[i]=1ll*a[i]*INV%mod;
    	for(register int i=0;i<=M;i++)g[i]=1ll*inv[i]*a[M+i]%mod;
    	for(register int i=M+1;i<k;i++)g[i]=0;
    	NTT(f,1);NTT(g,1);
    	for(register int i=0;i<k;i++)f[i]=1ll*f[i]*g[i]%mod;
    	NTT(f,-1);
    	for(register int i=0;i<=(M<<1);i++)f[i]=1ll*f[i]*INV%mod;
    	if((M<<1)!=m){
    		for(register int i=m;i;i--)
    			f[i]=MOD(f[i-1]+1ll*(m-1)*f[i]%mod);
    		f[0]=1ll*f[0]*(m-1)%mod;
    	}
    }
    int main(){
    	scanf("%d%d%d",&n,&A,&B);
    	if(A+B-2<0||A-1<0||B-1<0||A+B-2>n-1){puts("0");return 0;}
    	Preprocess();
    	if(n==1){if(A+B-2==0)printf("%d
    ",C(A+B-2,A-1));else puts("0");return 0;}
    	Solve(n-1);
    	printf("%lld
    ",1ll*f[A+B-2]*C(A+B-2,A-1)%mod);
    	return 0;
    }
    
  • 相关阅读:
    Working with macro signatures
    Reset and Clear Recent Items and Frequent Places in Windows 10
    git分支演示
    The current .NET SDK does not support targeting .NET Core 2.1. Either target .NET Core 2.0 or lower, or use a version of the .NET SDK that supports .NET Core 2.1.
    Build website project by roslyn through devenv.com
    Configure environment variables for different tools in jenkins
    NUnit Console Command Line
    Code Coverage and Unit Test in SonarQube
    头脑王者 物理化学生物
    头脑王者 常识,饮食
  • 原文地址:https://www.cnblogs.com/ForwardFuture/p/11522786.html
Copyright © 2020-2023  润新知