问题描述
给定若干组序列, 求序列中的逆序对的数量
数据范围
0≤N<500000,
0≤ai≤999999999
样例
Sample Input
5
9
1
0
5
4
3
1
2
3
0
Sample Output
6
0
思路一: 树状数组
我们用一个结构体记录下数组的值,和他对应的下标,将数组排序,把每个点从小到大遍历,并把这个点加入到c数组中,若干存在和这个点的逆序对,那么就是当前排序后该点的下标-sum(原来的下标),若干当前下标和原来的下标相同,就代表他位置没有变,他不存在逆序对,如果变了,就存在逆序对,累加他们的总和,就是结果。
代码
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <iostream>
using namespace std;
typedef long long ll;
const int N = 500010;
struct node
{
ll val;
int pos;
bool operator<(const node &a) const
{
if(a.val != val)
return val < a.val;
return pos < a.pos;
}
};
node a[N];
int c[N];
int n;
int lowbit(int x)
{
return x & (-x);
}
void add(int pos, int n)
{
for (int i = pos; i <= n; i += lowbit(i))
c[i]++;
}
ll get_sum(int pos, int n)
{
ll ans = 0;
for (int i = pos; i > 0; i -= lowbit(i))
{
ans += c[i];
}
return ans;
}
int main()
{
while (scanf("%d", &n) != EOF && n)
{
memset(c, 0, sizeof c);
for (int i = 1; i <= n; i++)
{
scanf("%lld", &a[i].val);
a[i].pos = i;
}
sort(a + 1, a + 1 + n);
ll sum = 0;
for (int i = 1; i <= n; i++)
{
add(a[i].pos, n);
sum += i - get_sum(a[i].pos, n);
}
printf("%lld\n", sum);
}
return 0;
}
思路二:归并排序
我们在归并排序的过程中,如果左边的数组中存在a[i]大于右边的a[j],那么i后面的后j都构成逆序对,那么逆序对数量加mid - i + 1。
代码
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <iostream>
using namespace std;
typedef long long ll;
const int N = 500010;
ll a[N];
int n;
ll t[N];
ll merge_sort(int l, int r)
{
if (l >= r)
return 0;
int mid = (l + r) >> 1;
int i = l, j = mid + 1, k = 0;
ll ans = 0;
ans += merge_sort(l, mid) + merge_sort(mid + 1, r);
while (i <= mid && j <= r)
{
if (a[i] <= a[j])
t[++k] = a[i++];
else
{
t[++k] = a[j++];
ans += mid - i + 1;
}
}
while (i <= mid)
t[++k] = a[i++];
while (j <= r)
t[++k] = a[j++];
for (int i = l, j = 1; i <= r; i++)
{
a[i] = t[j++];
}
return ans;
}
int main()
{
while (scanf("%d", &n) != EOF && n)
{
for (int i = 1; i <= n; i++)
scanf("%d", &a[i]);
printf("%lld\n", merge_sort(1, n));
}
return 0;
}