POJ 2299 Ultra-QuickSort(逆序数)

这题就是求逆序数。

逆序数定义:在一个排列中,如果一对数的前后位置与大小顺序相反,即前面的数大于后面的数,那么它们就称为一个逆序。一个排列中逆序的总数就称为这个排列的逆序数。

如2431中,21,43,41,31是逆序,逆序数是4。直白算法就是2后面比2小的数有1个,4后面比4小的数有2个,3后面比3小的数有1个,1后面比1小的数有0个,逆序数是1+2+1+0=4.

用树状数组求逆序数,用到离散化的方法。其基本思想就是在众多可能的情况中“只考虑我需要用的值”。忽略数的大小,只考虑数在排序前后所在的位置。

树状数组求解的思路:开一个能大小为这些数的最大值的树状数组,并全部置0。从头到尾读入这些数,每读入一个数就更新树状数组,查看它前面比它小的已出现过的有多少个数sum,然后用当前位置减去该sum,就可以得到当前数导致的逆序对数了。把所有的加起来就是总的逆序对数。

#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
using namespace std;

const int N = 500005;

struct Node
{
    int val;
    int pos;
};

Node node[N];
int c[N], reflect[N], n;

bool cmp(const Node& a, const Node& b)
{
    return a.val < b.val;
}

int lowbit(int x)
{
    return x & (-x);
}

void update(int x)
{
    while (x <= n)
    {
        c[x] += 1;
        x += lowbit(x);
    }
}

int getsum(int x)
{
    int sum = 0;
    while (x > 0)
    {
        sum += c[x];
        x -= lowbit(x);
    }
    return sum;
}

int main()
{
    while (scanf("%d", &n) != EOF && n)
    {
        memset(c,0,sizeof(c));
        for (int i = 1; i <= n; ++i)
        {
            scanf("%d", &node[i].val);
            node[i].pos = i;
        }
        sort(node + 1, node + n + 1, cmp);   //排序
        for (int i = 1; i <= n; ++i)
            reflect[node[i].pos] = i;   //离散化reflect[i]=j表示原来排第i位,现在排第j位,忽略了数值本身的大小
        long long ans = 0;
        for (int i = 1; i <= n; ++i)
        {
            update(reflect[i]);
            ans += i - getsum(reflect[i]);
        }
        printf("%lld\n", ans);
    }
    return 0;
}


你可能感兴趣的:(ACM,逆序数,树状数组,RMQ,POJ解题报告)