题目链接:
http://poj.org/problem?id=2299
题目大意:
给你N个数组成的序列,求这个序列的逆序数一共有多少个。这个序列个数不超过100000.
解题思路:
很经典的一个问题,解决方法有两种:第一种方法是归并排序,第二种方法是树状数组。
如果我们要枚举的话,复杂度为o(n*n),铁定TLE,所以要考虑更高效的算法。归并操作中,当取到右边集合的第i个元素时,左边集合中元素的个数就是这个元素的逆序数,以此类推,把右边集合全部的逆序数求出来,求和,就是这个序列的逆序数。
题目大意:
给你N个数组成的序列,求这个序列的逆序数一共有多少个。这个序列个数不超过100000.
解题思路:
很经典的一个问题,解决方法有两种:第一种方法是归并排序,第二种方法是树状数组。
如果我们要枚举的话,复杂度为o(n*n),铁定TLE,所以要考虑更高效的算法。归并操作中,当取到右边集合的第i个元素时,左边集合中元素的个数就是这个元素的逆序数,以此类推,把右边集合全部的逆序数求出来,求和,就是这个序列的逆序数。
代码如下:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<climits>
#include<cstdlib>
using namespace std;
#define N 1000010
int a[N], t[N];
long long sum;
void merge(int low, int mid, int high) //归并操作
{
int k, begin1, begin2, end1, end2;
begin1 = low;
end1 = mid;
begin2 = mid + 1;
end2 = high;
//比较两个指针所指向的元素,选择相对小的元素放入到合并空间,并移动指针到下一位置
for(k = 0; begin1 <= end1 && begin2 <= end2; ++k)
{
if(a[begin1] <= a[begin2]) //小的传送至辅助数组
t[k] = a[begin1++];
else
{
t[k] = a[begin2++];
sum += mid - begin1 + 1; //左边剩下的数个数全是逆序数
}
}
if(begin1 <= end1) //若第一个序列有剩余,直接拷贝出来粘到合并序列尾
memcpy(t + k, a + begin1, (end1 - begin1 + 1) * sizeof(int));
else //若第二个序列有剩余,直接拷贝出来粘到合并序列尾
memcpy(t + k, a + begin2, (end2 - begin2 + 1) * sizeof(int));
//将排序好的序列拷贝回数组中
memcpy(a + low, t, (high - low + 1) * sizeof(int));
}
void merge_sort(int begin, int end)
{
int mid;
if(begin < end)
{
mid = (end + begin) / 2;
merge_sort(begin, mid);
merge_sort(mid + 1, end);
merge(begin, mid, end);
}
}
int main()
{
int ncase;
int num;
while(scanf("%d", &num) && num)
{
sum = 0;
for(int i = 0; i < num; ++i)
scanf("%d", &a[i]);
merge_sort(0, num - 1);
printf("%lld\n", sum);
}
return 0;
}