[NOI2018]冒泡排序

题意可以转化成一个点不能既向左又向又移动。

条件成立当且仅当对于任意一个点,不同时存在左边比它的大点和右边比它小的点,即一个点如果不是前缀最大值就是后缀最小值。

假设不考虑字典序,有f[i][j]代表考虑前i个点,当前最大值为j的方案数,考虑到如果第i个数如果不填j就只能填所有能填的数中最小的,有转移f[i][j]+=f[i-1][k](k<=j),即f[i][j]=f[i-1][j]+f[i][j-1],发现就是卡特兰数。

考虑枚举p与q第一个不同的位置i,pi可以填的最小的数为qi的前缀最大值+1,卡特兰数统计一下贡献即可。

#include<bits/stdc++.h>
#define file(s) freopen(s".in","r",stdin);freopen(s".out","w",stdout);
#define P 998244353
#define mid (l+r>>1)
#define N 2100000
#define M 1300000
#define lb(x) (x&(-x))
#define inf 999999999
#define ll long long
#define mem(x) memset(x,0,sizeof(x));
using namespace std;
int T,n,q[N],mi[N],mn,mx;
ll ans,fac[N],nfac[N];
ll C(int x,int y){ return x<y||x<0||y<0?0:fac[x]*nfac[y]%P*nfac[x-y]%P;}
ll CT(int x,int y){int u=n-y,v=n-x;return (P+C(u+v,u)-C(v+u,u-1))%P;}
int qpow(ll x,ll y){
	ll res=1;
	for(;y;x=x*x%P,y>>=1) if(y&1) res=res*x%P;
	return res;
}
int main(){
	//freopen("1.txt","r",stdin); 
	scanf("%d",&T);
	fac[0]=1;for(int i=1;i<=M;i++) fac[i]=fac[i-1]*i%P;
	nfac[M]=qpow(fac[M],P-2);for(int i=M-1;~i;i--) nfac[i]=nfac[i+1]*(i+1)%P;
//	printf("%lld
",C(100,20));
	while(T--){
		scanf("%d",&n);mn=inf,ans=mx=0;
		mem(mi);
		for(int i=1;i<=n;i++) scanf("%d",&q[i]);
		for(int i=n;i;i--) if(q[i]<mn)mi[i]=1,mn=q[i];
		for(int i=1;i<=n;i++){
			mx=max(mx,q[i]);
	//		printf("%lld
",C(i-1,mx+1));
			(ans+=CT(i-1,mx+1))%=P;
			if(!mi[i]&&q[i]<mx) break;
		}
		printf("%lld
",ans);
	}
	return 0;
}

  

原文地址:https://www.cnblogs.com/blogoflyn/p/13269683.html