1. 程式人生 > 實用技巧 >[NOI2018]氣泡排序

[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\n",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\n",C(i-1,mx+1));
			(ans+=CT(i-1,mx+1))%=P;
			if(!mi[i]&&q[i]<mx) break;
		}
		printf("%lld\n",ans);
	}
	return 0;
}