链接: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\n", ans); return 0; }