例题
「POJ2299」Ultra-QuickSort
请分析,对于一串数列,至少要交换多少次(相邻两个数交换)才能使该数列有序(从小到大)?
注意:思考下,当存在两个数相等时,应该如何处理
输入
第一行:一个整数n<500000——序列的长度;
以下n行每行包括一个整数 0 ≤ a[i] ≤ 999,999,999.
输出
一个整数表示要交换的次数
样例输入
5
9
1
0
5
4
样例输出
6
分析
求逆序对,显然用归并排序。
当然,更好的方法是树状数组。但是a[i]太大,所以需要离散化。
代码
归并排序
#include<bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
#define full(a,b,c) fill(b,b+sizeof a/4,c)
#define N 500000+5
int n,arr[N];
int c[N];
long long ans;
void msort(int left,int right)
{
if(left==right)return;
int mid=(left+right)>>1;
msort(left,mid);
msort(mid+1,right);
int i=left,j=mid+1,k=left;
while(i<=mid&&j<=right)
if(arr[i]<=arr[j])//!!! if是<=,else则是>
c[k++]=arr[i++];
else c[k++]=arr[j++],ans+=(long long)mid-i+1;//第i个数比后面mid-i+1都小
while(i<=mid)c[k++]=arr[i++];
while(j<=right)c[k++]=arr[j++];
for(int i=left; i<=right; i++)arr[i]=c[i];
}
int main()
{
scanf("%d",&n);
for(int i=1; i<=n; i++)
scanf("%d",&arr[i]);
msort(1,n);
printf("%lld",ans);
}
树状数组+离散化
#include<bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
#define full(a,b) memset(a,b,sizeof a)
#define N 500000+5
#define lowbit(x) x&(-x)
struct node{
int s,t;
}arr[N];
int n,tree[N],za[N];
long long ans;
bool cmp(node x,node y)
{return x.s<y.s;}
void update(int x,int y){
for(int i=x;i<=N;i+=lowbit(i))
tree[i]+=y;
}
int sum(int x){
int ret=0;
for(int i=x;i>0;i-=lowbit(i))
ret+=tree[i];
return ret;
}//常规套路
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%d",&arr[i].s);//输入原值
arr[i].t=i;//原位置
}
sort(arr+1,arr+1+n,cmp);//按从小到大排序
arr[0].s=-1;//方便后面循环
for(int i=1;i<=n;i++){
za[arr[i].t]=i;//原位置为t,新位置为i
if(arr[i].s==arr[i-1].s)//与前一个数值相等
za[arr[i].t]=za[arr[i-1].t];
}
for(int i=n;i>=1;i--){//逆序,保证只会找后面的数
ans+=1LL*sum(za[i]-1);//找比i小的
update(za[i],1);
}
printf("%lld",ans);
}