树状数组
可以理解树状数组为前缀和
用树状数组来代表a[1],a[2],....,a[x]中小于等于a[x]的数字个数,那么小于等于a[x]是不会产生逆序数的,
所以a[x]产生的逆序数=目前总数-小于等于a[x]的数字=x-小于等于a[x]的数字
比如
5 4 2 6 3 1
i | 目前小于等于a[i] 个数 | 产生逆序数 |
0 | 1 | 0 |
1 | 1 | 1 |
2 | 1 | 2 |
3 | 4 | 0 |
4 | 2 | 3 |
5 | 1 | 5 |
如果数据量大可以考虑离散化
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <vector>
using namespace std;
const int N=5e5+5;
vector<int> v;
int a[N],c[N],n;
int get_id(const int& x){
return lower_bound(v.begin(),v.end(),x)-v.begin()+1;
}
int low_bit(const int& x){
return x&(-x);
}
void update(int x){
while(x<=n){
++c[x];
x+=low_bit(x);
}
}
int get_sum(int x){
int result=0;
while(x>0){
result+=c[x];
x-=low_bit(x);
}
return result;
}
int main(){
scanf("%d",&n);
for(int i=0;i<n;++i){
scanf("%d",&a[i]);
v.push_back(a[i]);
}
sort(v.begin(),v.end());
v.erase(unique(v.begin(),v.end()),v.end());
long long cnt=0;
for(int i=0;i<n;++i){
int id=get_id(a[i]);
update(id);
cnt+=i+1-get_sum(id);
}
printf("%lld\n",cnt);
return 0;
}
归并
比如归并[left,mid] [mid+1,right]
每次从左边数组和右边数组取一个数字,
如果右边的数字比较小,那产生的逆序数就是左边数组剩余的数量
#include <iostream>
#include <cstdio>
#include <cstring>
const int N=5e5+5;
int a[N],c[N];
long long int cnt;
void merge(int left,int mid,int right){
int i=left,j=mid+1,k=0;
while(i<=mid&&j<=right){
if(a[i]<=a[j]){
c[k]=a[i];
++i;
}
else{
cnt+=mid-i+1;
c[k]=a[j];
++j;
}
++k;
}
while(i<=mid){
c[k]=a[i];
++i;
++k;
}
while(j<=right){
c[k]=a[j];
++j;
++k;
}
memcpy(a+left,c,sizeof(int)*k);
}
void merge_sort(const int& left,const int& right){
if(left<right){
int mid=(left+right)>>1;
merge_sort(left,mid);
merge_sort(mid+1,right);
merge(left,mid,right);
}
}
int main(){
int n;
scanf("%d",&n);
for(int i=0;i<n;++i){
scanf("%d",&a[i]);
}
merge_sort(0,n-1);
printf("%lld\n",cnt);
return 0;
}
只看洛谷1908
归并貌似比树状数组快