链接:https://www.nowcoder.com/acm/contest/77/A
来源:牛客网
时间限制:C/C++ 2秒,其他语言4秒
空间限制:C/C++ 131072K,其他语言262144K
64bit IO Format: %lld
空间限制:C/C++ 131072K,其他语言262144K
64bit IO Format: %lld
题目描述
在一个排列中,如果一对数的前后位置与大小顺序相反,即前面的数大于后面的数,那么它们就称为一个逆序。一个排列中逆序的总数就称为这个排列的逆序数。比如一个序列为4 5 1 3 2, 那么这个序列的逆序数为7,逆序对分别为(4, 1), (4, 3), (4, 2), (5, 1), (5, 3), (5, 2),(3, 2)。
输入描述:
第一行有一个整数n(1 <= n <= 100000), 然后第二行跟着n个整数,对于第i个数a[i],(0 <= a[i] <= 100000)。
输出描述:
输出这个序列中的逆序数
示例1
输入
5 4 5 1 3 2
输出
7
思路:用树状数组,由于数据范围广,数据分散,不连续,故用结构体记录每个数字出现的顺序以及值,离散一下:
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
using namespace std;
int sum[100005]; //sum[x]表示的是区间为2^k个元素的和,k为x的二进制末尾0的个数,以为最后一个元素为a[x],
int n; //所以sum[x] = a[n - 2^k + 1] + ...+ a[x]; 看图理解
struct node{
int pos, x;
}a[100005];
int b[100005];
bool cmp(node x, node y){
return x.x < y.x;
}
void update(int pos, int val){
while(pos <= n){ //一直更新到最后
sum[pos] += val;
pos += (pos & (-pos)); //pos && (-pos)找到pos二进制末尾一个1
}
}
int query(int pos){
int ans = 0;
while(pos > 0){ //一直加到0
ans += sum[pos];
pos -= (pos & (-pos));
}
return ans;
}
int main(){
long long ans = 0, x; //不用Longlong会GG
scanf("%d", &n);
for(int i = 1; i <= n; i++){
scanf("%d", &a[i].x);
a[i].pos = i;
}
sort(a + 1, a + n + 1, cmp);
for(int i = 1; i <= n; i++){
b[a[i].pos] = i;
}
for(int i = 1; i <= n; i++){
x = b[i];
update(x, 1);
int temp = query(x);
ans += x - temp;
}
printf("%lld
", ans);
return 0;
}