(树状数组求逆序数+离散化+归并排序求逆序数)POJ 2299 Ultra-QuickSort

问题:Ultra-QuickSort POJ - 2299

多组数据,每组一个序列,n个元素(n不大于500000,每个元素不大于999,999,999),求按照冒泡排序排序,移动相邻两个元素的次数是多少?

分析:实际就是求逆序数,题虽水,但是思路很棒,收获很大

一、树状数组求逆序数+离散化

树状数组的博客,看这个就够:点击打开链接

仔细说说离散化的问题,由于n个数远远小于实际范围,我们为了节约内存,不开一个999,999,999大的数组,只开一个500000的数组,就需要把这500000个落在999,999,999范围内的数映射到范围在500000之内,这一步是绝对可行的,比如序列:9,1,0,5,4;逆序数与5,2,1,4,3完全一样,而后者的数值范围明显缩小,这就是我们的思想

具体操作是:先设置node结构体,存储值和下标,然后按照值排序,那么下标被打乱,这个时候第一个值对应的映射值就是1,第二个值对应的映射值就是2,……而且由于一开始的下标与值捆绑存储,我们很容易就知道一个值的映射值原先在哪一个位置(就是下标),那么还原就好了……有点绕啊……

那么如何利用树状数组呢?由于是从小到大排序,我们对序列就要检索前面的元素大于后面的元素的情况数,也就是说,判断某一个元素产生的逆序数总数,是需要利用它后面的所有元素的,所以不如先从最后一个元素开始,把这个元素对应的值(现在已经是映射值了)的数量加一,即add操作,然后检索,值小于其的元素已经加入几个了,因为只要是加入了的,都是下标在其后面的,这个时候就是使用getsum(i)-1来计算了,把这些结果累加,就是答案

#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
typedef long long ll;
const int num=500010;
int n;
ll a[num],c[num],tem[num];
void add(int i,int value){
	a[i]+=value;
	while(i<=n){
		c[i]+=value;
		i+=i&(-i);
	}
}
int getsum(int i){
	int s=0;
	while(i>0){
		s+=c[i];
		i-=i&(-i);
	}
	return s;
}
struct node{
	int val,id;
	bool operator < (const node& c){
		return val<c.val;
	}
}nodes[num];
int main(){
	cin>>n;
	while(n!=0){
		memset(a,0,sizeof(a));
		memset(c,0,sizeof(c));
		ll SUM=0;
		for(int i=1;i<=n;i++){
			cin>>nodes[i].val;
			nodes[i].id=i;
		}
		sort(nodes+1,nodes+1+n);
		for(int i=1;i<=n;i++){//完成离散化 
			tem[nodes[i].id]=i;
		}
		for(int i=n;i>=1;i--){
			add(tem[i],1);
			SUM+=(getsum(tem[i])-1);
		}
		cout<<SUM<<endl; 
		cin>>n;
	}
	return 0;
}

二、归并排序求逆序数

只贴代码,详见我的另外一篇博客:点击打开链接

#include<iostream>
using namespace std;
typedef long long ll;
const int num=500010;
int a[num],tem[num];
ll sum;
void Merge(int a[],int s,int mid,int e,int tem[]){
	int p=0,pi=s,pj=mid+1;
	while(pi<=mid&&pj<=e){
		if(a[pi]<a[pj]){
			tem[p++]=a[pj++];
		}
		else if(a[pi]>=a[pj]){
			tem[p++]=a[pi++];sum+=(e-pj+1);
		}
	}
	while(pi<=mid)tem[p++]=a[pi++];
	while(pj<=e)tem[p++]=a[pj++];
	for(int i=0;i<e-s+1;i++){ 
		a[s+i]=tem[i];
	}
}
void MergeSort(int a[],int s,int e,int tem[]){
	if(s<e){
		int mid=s+(e-s)/2;
		MergeSort(a,s,mid,tem);
		MergeSort(a,mid+1,e,tem);
		Merge(a,s,mid,e,tem);
	}
}
int main(){
	int n;cin>>n;
	while(n!=0){
		sum=0;
		for(int i=0;i<n;i++){
			cin>>a[i];
		}
		MergeSort(a,0,n-1,tem);
		cout<<sum<<endl;
		cin>>n;
	}	
	return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_41333528/article/details/80273262