• P4769-[NOI2018]冒泡排序【组合数学,树状数组】


    正题

    题目链接:https://www.luogu.com.cn/problem/P4769


    题目大意

    有一个冒泡排序的算法

    输入:一个长度为 n 的排列 p[1...n]
    输出:p 排序后的结果。
    for i = 1 to n do
    	for j = 1 to n - 1 do
    		if(p[j] > p[j + 1])
    			交换 p[j] 与 p[j + 1] 的值
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    然后给出一个排列 a a a,求在所有字典序大于 a a a的排列 p p p中冒泡排序交换次数恰好为 ∑ i = 1 n ∣ i − p i ∣ \sum_{i=1}^n|i-p_i| i=1nipi的排列数。

    1 ≤ n ≤ 6 × 1 0 5 , ∑ n ≤ 2 × 1 0 6 1\leq n\leq 6\times 10^5,\sum n\leq 2\times 10^6 1n6×105,n2×106


    解题思路

    打一下表发现合法的排列条件是最长下降子序列不超过 2 2 2

    然后我们先不考虑字典序限制条件怎么做,我们设 f i , 1 / 2 f_{i,1/2} fi,1/2表示目前下降子序列长度为 1 / 2 1/2 1/2中末尾最大的那个。

    那么 f i , 1 f_{i,1} fi,1就是目前出现的数中最大的,然后如果我们从前往后填数,那么如果 ≤ f i , 2 \leq f_{i,2} fi,2的数中有没有填进去的,肯定不合法,所以 f i , 2 f_{i,2} fi,2肯定比目前没有填进去的数中所有数字都小,不需要考虑。

    g i , j g_{i,j} gi,j表示目前还剩下 i i i个数没填,其中 f i , 1 f_{i,1} fi,1大于其中的 j j j个数,那么有 g i , j g_{i,j} gi,j可以转移到 g i − 1 , j − 1 g_{i-1,j-1} gi1,j1(填在最底)和 g i − 1 , k ( k ≥ j ) g_{i-1,k}(k\geq j) gi1,k(kj)(填在 j j j上面)。

    我们考虑快速的求出每个 g g g,反过来就是 g i , j g_{i,j} gi,j转移到 g i + 1 , j + 1 g_{i+1,j+1} gi+1,j+1 g i + 1 , j g_{i+1,j} gi+1,j

    我们维护一个 h i , j = g i , i − j h_{i,j}=g_{i,i-j} hi,j=gi,ij,那么每次的转移就是 h i , j h_{i,j} hi,j转移到 h i , k ( j ≤ k ≤ i ) h_{i,k}(j\leq k\leq i) hi,k(jki)

    这个转移很像卡特兰数的要求,每次可以往下或者往右,但是不能超过对角线。

    这样来说移动到位置 ( n , m ) ( m ≤ n ) (n,m)(m\leq n) (n,m)(mn)的话方案数就是 ( n + m m ) − ( n + m m − 1 ) \binom{n+m}{m}-\binom{n+m}{m-1} (mn+m)(m1n+m)

    然后就是枚举第一个超过该字典序的位置,这样前面的方案固定,剩下的数可以用树状数组计算得出,再用组合数求答案即可。

    时间复杂度: O ( n log ⁡ n ) O(n\log n) O(nlogn)


    code

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #define ll long long
    #define lowbit(x) (x&-x) 
    using namespace std;
    const ll N=6e5*2,P=998244353;
    ll T,n,t[N],a[N],fac[N],inv[N],ans;
    ll C(ll n,ll m)
    {if(m<0)return 0;return fac[n]*inv[m]%P*inv[n-m]%P;}
    void Change(ll x,ll val){
    	while(x<=n){
    		t[x]+=val;
    		x+=lowbit(x);
    	}
    	return;
    }
    ll Ask(ll x){
    	ll ans=0;
    	while(x){
    		ans+=t[x];
    		x-=lowbit(x);
    	}
    	return ans;
    }
    ll F(ll n,ll m){
    	m=n-1-m;if(!m)return 0;m--;
    	return (C(n+m,m)-C(n+m,m-1)+P)%P;
    }
    signed main()
    {
    //	freopen("inverse3.in","r",stdin);
    	fac[0]=inv[0]=inv[1]=1;
    	for(ll i=2;i<N;i++)inv[i]=P-inv[P%i]*(P/i)%P;
    	for(ll i=1;i<N;i++)fac[i]=fac[i-1]*i%P,inv[i]=inv[i-1]*inv[i]%P;
    	scanf("%lld",&T);
    	while(T--){
    		scanf("%lld",&n);ans=0;
    		for(ll i=1;i<=n;i++)
    			scanf("%lld",&a[i]),Change(a[i],1);
    		for(ll i=1,mx=0;i<=n;i++){
    			Change(a[i],-1);mx=max(mx,a[i]);
    			(ans+=F(n-i+1,Ask(mx)))%=P;
    			if(a[i]<mx&&Ask(a[i]))
    				break;
    		}
    		for(int i=1;i<=n;i++)t[i]=0;
    		printf("%lld\n",ans);
    	}
    	return 0; 
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
  • 相关阅读:
    阿里资深架构师整理分享的分布式系统架构:技术栈详解与进阶文档
    最后的防线:数据存储加密方案
    Java标识符和关键字,Java常量的定义和分类
    力扣每日一题67:二进制求和
    如何在 Apache Tomcat 中实现 SSL?
    EFCore学习笔记(2)——实体类型
    4G通信电子标签
    vite + vue3 + js 搭建组件库 + 核心配置与使用
    让Win11系统更好用的几个设置
    (科目三)数据库基础知识
  • 原文地址:https://blog.csdn.net/Mr_wuyongcong/article/details/125505635