POJ - 2299 Ultra-QuickSort 逆序对经典问题 (树状数组解法 VS 归并排序解法)

问题描述

给定若干组序列, 求序列中的逆序对的数量

数据范围

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;
}

发布了121 篇原创文章 · 获赞 223 · 访问量 3万+

猜你喜欢

转载自blog.csdn.net/qq_45432665/article/details/104965088