题目描述:给出一个序列,求出这个序列的逆序数。
逆序数:在一个排列中,如果一对数的前后位置与大小顺序相反,即前面的数大于后面的数,那么它们就称为一个逆序。一个排列中逆序的总数就称为这个排列的逆序数。一个排列中所有逆序总数叫做这个排列的逆序数。也就是说,对于n个不同的元素,先规定各元素之间有一个标准次序(例如n个 不同的自然数,可规定从小到大为标准次序),于是在这n个元素的任一排列中,当某两个元素的先后次序与标准次序不同时,就说有1个逆序。一个排列中所有逆序总数叫做这个排列的逆序数。
例如 序列 9 1 0 5 4 的逆序数为6
序列 1 2 3 4 5 的逆序数为0
树状数组解题思路:开一个能大小为这些数的最大值的树状数组,并全部置0。从头到尾读入这些数,每读入一个数就更新树状数组,查看它前面比它小的已出现过的有多少个数sum,然后用当前位置减去该sum,就可以得到当前数导致的逆序对数了。把所有的加起来就是总的逆序对数。
离散化:本题数字最大为999999999,如果直接开数组那会浪费大量空间,又由于本题的序列长度不超过500000,所以经过离散化之后开长度为500000的数组就行了。
例如 序列 9 1 0 5 4 离散化后为 5 2 1 4 3
序列 5 6 8 1 0 离散化后为 3 4 5 2 1
代码:
#include <map>
#include <set>
#include <cmath>
#include <queue>
#include <stack>
#include <cstdio>
#include <vector>
#include <iomanip>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
#define ll long long
#define mod 1000000007
#define mem(a) memset(a,0,sizeof(a))
using namespace std;
const int maxn = 5e5 + 5 , inf = 0x3f3f3f3f ;
int A[maxn],C[maxn];
int n,t;
struct number{
int value,pos;
bool operator < (const number & n ) const {
return value < n.value;
}
};
number numbers[maxn];
int lowbit(int x){return x&(-x);}
void Updata(int x){
for(int i = x ; i <= n ; i += lowbit(i))
C[i] += 1;
}
int getSum(int x){
int ans = 0;
for(int i = x ; i > 0 ; i -= lowbit(i))
ans += C[i];
return ans;
}
int main(){
// freopen("in.txt","r",stdin);
// freopen("out.txt","w",stdout);
while(scanf("%d",&n)&&n){
mem(A);mem(C);mem(numbers);
//离散化
for(int i = 1 ; i <= n ; i ++ ){
scanf("%d",&numbers[i].value);
numbers[i].pos = i;
}
sort(numbers+1,numbers+n+1);
for(int i = 1 ; i <= n ; i ++ ){
A[numbers[i].pos] = i;
}
//ans要定义为long long
ll ans = 0;
for(int i = 1 ; i <= n ; i ++ ){
Updata(A[i]);
ans += i - getSum(A[i]);
}
printf("%lld\n",ans);
}
}