UOJ394 NOI2018 冒泡排序

Problem

UOJ

Solution

对于排列中的一个数,如果它前面有 k k k 个大于它的数,那么它一定会向前走 k k k 步,而这每一步都不能冗余才能达到下界,因此一个数的前面要么全都比它小,要么所有比它小的数都出现了在它的前面。

然后画画图,发现这其实等价于序列的最长下降子序列长度不超过2。这样我们就可以设 f [ i ] [ j ] f[i][j] f[i][j] 表示前 i i i 个数最大值为 j j j 的合法方案数,则我们每次仅能选取比 j j j 大的数或者未填的最小值,即可以转移至 f [ i + 1 ] [ j ⋯ n ] f[i+1][j\cdots n] f[i+1][jn]。当然 j = i j=i j=i 时不能转移至 f [ i + 1 ] [ j ] f[i+1][j] f[i+1][j]。把它画在坐标系上,则所有状态都在 y = x y=x y=x 上方的上三角中,然后你会发现 f [ i ] [ j ] f[i][j] f[i][j] 表示的其实就是 ( i , j ) (i,j) (i,j) 走到 ( n , n ) (n,n) (n,n) 且不跨越 y = x y=x y=x 的方案数。

而对于字典序的限制,我们可以枚举给定序列的一个前缀,使某一位字典序大于即可,即相当于 ( i , m x + 1 ) , ( i , m x + 2 ) ⋯ (i,mx+1),(i,mx+2)\cdots (i,mx+1),(i,mx+2) 都是可行的开始点,这又相当于是起始点为 ( i − 1 , m x + 1 ) (i-1,mx+1) (i1,mx+1) 。记 l i m = m x + 1 lim=mx+1 lim=mx+1,它关于 y = x − 1 y=x-1 y=x1 的对称点为 ( l i m + 1 , i − 2 ) (lim+1,i-2) (lim+1,i2),用组合数减一下可以算出它的贡献

( 2 n − i − l i m + 1 n − i + 1 ) − ( 2 n − i − l i m + 1 n − i + 2 ) \binom {2n-i-lim+1} {n-i+1}-\binom {2n-i-lim+1} {n-i+2} (ni+12nilim+1)(ni+22nilim+1)

时间复杂度 O ( ∑ n ) O(\sum n) O(n)

Code

#include 
using namespace std;
typedef long long ll;
const int N=1200000,maxn=1200010,mod=998244353;
template <typename Tp> inline int getmin(Tp &x,Tp y){return y<x?x=y,1:0;}
template <typename Tp> inline int getmax(Tp &x,Tp y){return y>x?x=y,1:0;}
template <typename Tp> inline void read(Tp &x)
{
    x=0;int f=0;char ch=getchar();
    while(ch!='-'&&(ch<'0'||ch>'9')) ch=getchar();
    if(ch=='-') f=1,ch=getchar();
    while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
    if(f) x=-x;
}
int z,n,mn,mx,y,dfc,ans,a[maxn],fac[maxn],inv[maxn],vis[maxn];
int max(int x,int y){return x>y?x:y;}
int pls(int x,int y){return x+y>=mod?x+y-mod:x+y;}
int dec(int x,int y){return x<y?x-y+mod:x-y;}
int c(int n,int m){return m>n?0:(ll)fac[n]*inv[m]%mod*inv[n-m]%mod;}
int power(int x,int y)
{
	int res=1;
	for(;y;y>>=1,x=(ll)x*x%mod)
	  if(y&1)
	    res=(ll)res*x%mod;
	return res;
}
int main()
{
	fac[0]=1;
	for(int i=1;i<=N;i++) fac[i]=(ll)fac[i-1]*i%mod;
	inv[N]=power(fac[N],mod-2);
	for(int i=N-1;~i;i--) inv[i]=(ll)inv[i+1]*(i+1)%mod;
	read(z);
	while(z--)
	{
		read(n);ans=mx=0;mn=1;++dfc;
		for(int i=1;i<=n;i++) read(a[i]);
		for(int i=1;i<=n;i++)
		{
			vis[a[i]]=dfc;y=max(mx,a[i])+1;
			if(y<=n) ans=pls(ans,dec(c(n+n-y-i+1,n-i+1),c(n+n-y-i+1,n-i+2)));
			if(a[i]>mx) mx=a[i];
			else if(a[i]!=mn) break;
			while(vis[mn]==dfc) ++mn;
		}
		printf("%d\n",ans);
	}
	return 0;
}

你可能感兴趣的:(=====动态规划=====,好题集,BZOJ,UOJ)