zoukankan      html  css  js  c++  java
  • 【UOJ#394】[NOI2018] 冒泡排序

    题目链接

    题意

    求有多少个字典序严格大于给定排列 (q_i) 的排列满足其逆序对数(冒泡排序需要交换的次数)达到下限 (frac{1}{2}sum_{i=1}^n |i-p_i|)

    Sol

    很神仙的一题。

    首先我们打表 (滑稽)
    发现当没有字典序限制时的答案就是卡特兰数。
    考虑感性理解,那么考虑卡特兰数的经典应用,它是最长下降子序列长度不超过 2 的排列的个数。
    发现很有道理啊 owo。

    于是我们就考虑在有字典序限制的条件下求解这个玩意。

    dp有点难想到,我们设 (f[i][j]) 表示已经填了的长度为 (i),还没有加入的数中小于当前排列中最大值的数的个数是 (j) 的方案数。

    为什么这样设状态? 因为我们发现最长下降子序列不能超过 2,在保证了已经加入的数合法的情况下,我们只需要关注最大值的情况就行了,小于最大值的数在后面都必须得升序排好,不然就会出现长度为 3 的下降序列。

    转移的话,两种决策,要么填入一个小于当前最大值的数转移到 (j-1) (注意这里一定是那个最小的数被填进去),或者是我们填入一个大于当前最大值的数,这样 (j) 势必不会减少,他的上限是 (n-i)

    初值是 (f[0][0]=1) , 要求的东西是 (f[n][0])
    那么这就像一个格路问题了。每次往右走一步的同时往上走任意步数或者往下一步(反正往上走多了也回不来了)
    但是不能直接求,我们做一个转化。
    容易发现往上和往下的步数是一样的,并且任意时刻不能走到 x 轴下方。
    我们考虑用括号序列来解决这个问题。
    每次就是加入任意数量左括号后加入一个右括号。
    方案数用格路问题套路解决就是:

    [{2*n-1choose n-1}-{2*n-1choose n+1} ]

    (其实和卡特兰数的某个通项公式一样的啦)

    这样我们得到一个复杂度为 (O(n^2)) 的做法。
    每次按照数位dp 一样枚举在哪里字典序开始大于给定排列。
    维护出这个时候选择恰好是 (q_i) 时应该有的左右括号个数。

    加入左括号代表数变大,所以我们枚举再加入多少个左括号就能算答案了。

    假设我们的左右括号数分别是 (l,r)

    贡献就是:

    [sum_{i=l+1} {2*n-i-rchoose n-r}-{2*n-i-rchoose n-r+1} ]

    做一个代换令 (x=n-i,y=n-r)

    [sum_{x=0}^{n-l-1}{x+ychoose y}-{x+ychoose y+1} ]

    常见的玩意了:

    [sum_{x=0}^{n-l-1}{x+ychoose y}-sum_{x=0}^{n-l-1}{x+ychoose y+1} ]

    [{n-l+ychoose y+1}-{n-l+ychoose y+2} ]

    [{2*n-l-rchoose y+1}-{2*n-l-rchoose y+2} ]

    code:

    #include<bits/stdc++.h>
    #define Set(a,b) memset(a,b,sizeof(a))
    using namespace std;
    const int mod=998244353;
    template <typename T> inline void init(T&x){
    	x=0;char ch=getchar();bool t=0;
    	for(;ch>'9'||ch<'0';ch=getchar()) if(ch=='-') t=1;
    	for(;ch>='0'&&ch<='9';ch=getchar()) x=(x<<1)+(x<<3)+(ch-48);
    	if(t) x=-x;return;
    }
    typedef long long ll;
    template<typename T>inline void Inc(T&x,int y){x+=y;if(x>=mod) x-=mod;return;}
    template<typename T>inline void Dec(T&x,int y){x-=y;if(x <  0) x+=mod;return;}
    template<typename T>inline int fpow(int x,T k){int ret=1;for(;k;k>>=1,x=(ll)x*x%mod) if(k&1) ret=(ll)ret*x%mod;return ret;}
    inline int Sum(int x,int y){x+=y;if(x>=mod) return x-mod;return x;}
    inline int Dif(int x,int y){x-=y;if(x < 0 ) return x+mod;return x;}
    const int N=6e5+10;
    const int MAXN=2e6+1;
    int P[N],n,num=0,Ct[MAXN];
    int f[N],fac[MAXN],finv[MAXN],Inv[MAXN];
    inline int C(int n,int m){return (n<m||m<0)? 0:((ll)fac[n]*finv[m]%mod*finv[n-m]%mod);}
    inline void Sieve(const int n=MAXN-1){
    	fac[0]=finv[0]=fac[1]=finv[1]=Inv[1]=1;
    	for(int i=2;i<=n;++i) fac[i]=(ll)fac[i-1]*i%mod,Inv[i]=(ll)(mod-mod/i)*Inv[mod%i]%mod,finv[i]=(ll)finv[i-1]*Inv[i]%mod;
    	Ct[0]=Ct[1]=1;
    	for(int i=2;i<n;++i) Ct[i]=(ll)((i<<2)-2)*Ct[i-1]%mod*Inv[i+1]%mod;
    	return;
    }
    inline int Cal(int l,int r){return Dif(C(2*n-l-r-1,n-r+1),C(2*n-l-r-1,n-r+2));}
    bool vis[N];
    #define lowbit(a) ((a)&(-a))
    int tr[N];
    inline void Update(int p){for(;p<=n;p+=lowbit(p))++tr[p];return;}
    inline int  Query(int p){int ret=0;for(;p;p-=lowbit(p)) ret+=tr[p];return ret;}
    inline int Solve(){
    	Set(vis,0),Set(tr,0);
    	int ans=0,mx=0;
    	int x=0,y=0;int low=1;int l=0,r=0;	
    	for(int i=1;i<=n;++i) {
    		int les=P[i]-1-Query(P[i]);++x;
    		if(P[i]>mx) {int d=les-y;y=les;l+=d+1;++r;}else --y,++r;
    		const int D=n-r;
    		Inc(ans,Dif(C(n-l+D,D+1),C(n-l+D,D+2)));
    		vis[P[i]]=1;while(vis[low]) ++low;
    		mx=max(P[i],mx);
    		if(mx>P[i]&&P[i]>low) break;
    		Update(P[i]);
    	}
    	return ans;
    }
    int main()
    {
    	Sieve();bool fl=1;
    	int T;init(T);
    	while(T--){init(n);
    		for(int i=1;i<=n;++i) {init(P[i]);if(P[i]!=i) fl=0;}
    		if(fl) printf("%d
    ",Ct[n]-1);
    		else printf("%d
    ",Solve());
    	}
    	return 0;
    }
    
    
  • 相关阅读:
    jsp Servlet 3.0文件上传
    Android 模拟多线程下载
    Android之Handler用法总结
    Android 手机模拟器安卓软件
    Json笔记
    java.lang.ClassNotFoundException: com.google.gson.Gson”
    关于MD5加密
    Android 抽屉布局
    Android ListView 适配器
    如何在安裝SELinux的环境执行Quartus II
  • 原文地址:https://www.cnblogs.com/NeosKnight/p/10803583.html
Copyright © 2011-2022 走看看