UOJ#394. 【NOI2018】冒泡排序

传送门

题解:
考场上真的有debuff啊,下来看是道傻逼题。

发现等价于最多由两个上升序列构成,然后就相当于记 f i , m x f_{i,mx} fi,mx表示当前填到第 i i i位,最大值为 j j j的方案数,下一个要么比 m x mx mx大,要么是小于 m x mx mx的第一个。

发现这就是一个括号序列,比mx大就相当于加入若干左括号,然后 i i i往后挪相当于加入右括号,可以卡特兰数快速算一下了。

对于每个固定的前缀,我们枚举这一位填什么(比原来大),发现是组合数列求和,可以 O ( 1 ) O(1) O(1)计算,总复杂度 O ( n ) O(n) O(n)

#include 
using namespace std;

const int RLEN=1<<18|1;
inline char nc() {
	static char ibuf[RLEN],*ib,*ob;
	(ib==ob) && (ob=(ib=ibuf)+fread(ibuf,1,RLEN,stdin));
	return (ib==ob) ? -1 : *ib++;
}
inline int rd() {
	char ch=nc(); int i=0,f=1;
	while(!isdigit(ch)) {if(ch=='-')f=-1; ch=nc();}
	while(isdigit(ch)) {i=(i<<1)+(i<<3)+ch-'0'; ch=nc();}
	return i*f;	
}

const int N=1e6+3e5, mod=998244353;
inline int add(int x,int y) {return (x+y>=mod) ? (x+y-mod) : (x+y);}
inline int dec(int x,int y) {return (x-y<0) ? (x-y+mod) : (x-y);}
inline int mul(int x,int y) {return (long long)x*y%mod;}
inline int power(int a,int b,int rs=1) {for(;b;b>>=1,a=mul(a,a)) if(b&1) rs=mul(rs,a); return rs;}

struct Combin {
	int fac[N],ifac[N];
	Combin() {
		fac[0]=1;
		for(int i=1;i<N;i++) fac[i]=mul(fac[i-1],i);
		ifac[N-1]=power(fac[N-1],mod-2);
		for(int i=N-2;~i;i--) ifac[i]=mul(ifac[i+1],i+1);
	}
	inline int C(int a,int b) {return mul(fac[a],mul(ifac[b],ifac[a-b]));}
} C;

int n,mx,mn,p[N],c[N];
inline void solve() {
	n=rd(); mx=0; mn=1;
	for(int i=1;i<=n;i++) p[i]=rd(), c[i]=0;
	int ans=0, tag=1;
	for(int i=1;i<=n;i++) {
		int x=p[i]; c[x]=1;
		int lim=max(mx+1,x+1);
		if(lim<=n) {
			ans=add(ans,C.C(2*n-lim-i+1,n-i+1));
			if(lim<n) ans=dec(ans,C.C(2*n-lim-i+1,n-i+2));
		}
		if(x>mx) mx=x;
		else if(x!=mn) break;
		while(c[mn]) ++mn;
	} 
	printf("%d\n",ans);
}
int main() {
	for(int i=rd();i;i--) solve();
}

你可能感兴趣的:(排列组合)