• [NOI Online 提高组]冒泡排序(树状数组)


    [NOI Online 提高组]冒泡排序(树状数组)

    题面

    分析

    (f_i=sum_{j=0}^i[a_j>a_i])表示([1,i-1])中比(a_i)大的数的个数。那么逆序对数就是(sum_{i=1}^n f_i).可以用树状数组预处理得出。

    手玩一下冒泡排序过程发现,每冒泡一轮,所有的(f_i)会-1(减到0为止),然后会向前移动一位,即(f_{i-1} leftarrow f_i-1).
    那么冒泡(k)轮之后,(f_1,f_2,dots f_k)会消失,所有(leq k)(f_i)会变为0。 注意到对于(i in [1,k]),(f_i < k).因此这些数都会减为0,那么我们就不需要考虑平移,只需要考虑统计大于(k)的数,也就是说,(f_i)的位置没有意义.(比赛时没想到这一点,硬上一个树套树被卡成暴力分)

    那么答案就是:

    [sum_{i=1}^n f_i[f_i>k]-ksum_{i=1}^n[f_i>k] ]

    因此我们要统计出(>k)(f_i)的值之和,以及它们的个数。可以用两个树状数组实现。由于(f_i<n),我们可以把每个(f_i)的值存储在树状数组的第(f_i)位,查询时区间求和即可。

    最后考虑修改。修改的时候只会改变(f_x)(f_{x+1})的值,若(a_x>a_{x+1}),则交换后比(a_{x+1})小的数少了一个,那么(f_{x+1})-1.反之(f_x)+1,然后交换(f_x,f_{x+1}). 可以用树状数组单点修改实现

    代码

    #include<iostream>
    #include<cstdio>
    #include<utility>
    #include<cstring>
    #define maxn 200000
    using namespace std;
    typedef long long ll; 
    int n,m,a[maxn+5],f[maxn+5];
    struct fenwick_tree{
    	ll c[maxn+5];
    	inline int lowbit(int x){
    		return x&-x;
    	}
    	void ini(){
    		for(int i=1;i<=n;i++) c[i]=0;
    	}
    	void add(int x,int v){
    		if(x==0) return;
    		for(int i=x;i<=n;i+=lowbit(i)) c[i]+=v;
    	}
    	ll sum(int x){
    		ll ans=0;
    		for(int i=x;i>0;i-=lowbit(i)) ans+=c[i];
    		return ans; 
    	}
    	ll query(int l,int r){
    		return sum(r)-sum(l-1);
    	}
    }T1,T2,T3;
    void insert_f(int val){
    	T2.add(val,1);
    	T3.add(val,val);
    }
    void del_f(int val){
    	T2.add(val,-1);
    	T3.add(val,-val);
    }
    pair<ll,ll>query_f(int l,int r){
    	return make_pair(T2.query(l,r),T3.query(l,r));
    }
    void change(int x){
    	del_f(f[x]);
    	del_f(f[x+1]);//先把原来的去掉
    	if(a[x]<a[x+1]) f[x]++;
    	else f[x+1]--;
    	swap(f[x],f[x+1]);
    	swap(a[x],a[x+1]); 
    	insert_f(f[x]);
    	insert_f(f[x+1]);
    }
    ll query(int k){
    	if(k>=n) return 0;
    	pair<ll,ll>tmp=query_f(k+1,n);
    	//对于位置在k之前的,逆序对数一定<k,所以会被减成0
    	//我们只需要算所有值>k的即可,不需要考虑位置
    	return tmp.second-tmp.first*k; 
    }
    int main(){
    	int cmd,x;
    	scanf("%d %d",&n,&m);
    	for(int i=1;i<=n;i++) scanf("%d",&a[i]);
    	for(int i=1;i<=n;i++){
    		f[i]=i-1-T1.sum(a[i]);
    		T1.add(a[i],1);
    	}
    	for(int i=1;i<=n;i++) insert_f(f[i]);
    	for(int i=1;i<=m;i++){
    		scanf("%d %d",&cmd,&x);
    		if(cmd==1) change(x);
    		else printf("%lld
    ",query(x));
    	}
    }
    
    
  • 相关阅读:
    P1342 请柬
    P1186 玛丽卡
    Scala 中下划线的用法
    IDEA2017 maven Spark HelloWorld项目(本地断点调试)
    Spark内存管理详解
    Spark基础知识
    scala基本语法
    分布式锁的一点理解
    Redis并发问题
    redis集群原理
  • 原文地址:https://www.cnblogs.com/birchtree/p/12562619.html
Copyright © 2020-2023  润新知