[NOI2018]氣泡排序
阿新 • • 發佈:2020-07-09
題意可以轉化成一個點不能既向左又向又移動。
條件成立當且僅當對於任意一個點,不同時存在左邊比它的大點和右邊比它小的點,即一個點如果不是字首最大值就是字尾最小值。
假設不考慮字典序,有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; }