[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; }
posted on 2020-07-08 22:32 啊啊啊起个什么名字好 阅读(106) 评论(0) 编辑 收藏 举报