问题: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; }