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;
    }
    
    
  • 相关阅读:
    hihoCoder #1062 : 最近公共祖先·一
    hihoCoder #1050 : 树中的最长路
    hihoCoder #1049 : 后序遍历
    108 Convert Sorted Array to Binary Search Tree 将有序数组转换为二叉搜索树
    107 Binary Tree Level Order Traversal II 二叉树的层次遍历 II
    106 Construct Binary Tree from Inorder and Postorder Traversal 从中序与后序遍历序列构造二叉树
    105 Construct Binary Tree from Preorder and Inorder Traversal 从前序与中序遍历序列构造二叉树
    104 Maximum Depth of Binary Tree 二叉树的最大深度
    102 Binary Tree Level Order Traversal 二叉树的层次遍历
    101 Symmetric Tree 判断一颗二叉树是否是镜像二叉树
  • 原文地址:https://www.cnblogs.com/NeosKnight/p/10803583.html
Copyright © 2011-2022 走看看