[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]-k\sum_{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\n",query(x));
	}
}

posted @ 2020-03-24 22:24  birchtree  阅读(243)  评论(0编辑  收藏  举报