poj 2299 Ultra-QuickSort(数学:求逆序数||数据结构:树状数组)

求一个数组的逆序数

因为数据较大所以不能用冒泡排序

这里用到归并排序...到现在不看书还是写不出来尴尬

注意这里在归并排序过程中求逆序数个数的方法!

证明如下:

对于当前两个数组,a和b分别有序

合并a[i] b[j]的过程中我们这样考虑逆序数的个数:

如果a[i] > b[j],则a[i]后面的数必然都大于b[j],针对b[j]逆序数为a[i]后面数的个数,即len(a)-i+1

如果a[j] <= b[j], 则a[i]后面的数与b[j]的大小关系无法判断,我们只能接着判断a[i+1]和b[j]的关系

而针对b中每个元素的逆序数之和必然为总的逆序数

代码如下:

#include <cstdio>
#include <iostream>
#include <algorithm>
#define MAXN 500010
#define LL long long
using namespace std;

int a[MAXN],tmp[MAXN];
LL ans;

void merge(int L, int R, int M) {
    int i, j, k = 1;
    for(i=L,j=M+1; i<=M&&j<=R; ) {
        if(a[i] < a[j]) {
            tmp[k++] = a[i++];
        }
        else {
            tmp[k++] = a[j++];
            ans += M-i+1;
        }
    }
    for( ; i<=M; )
        tmp[k++] = a[i++];
    for( ; j<=R; )
        tmp[k++] = a[j++];
    for(i=1; i<k; ++i)
        a[L+i-1] = tmp[i];
}

void mergesort(int front, int end) {
    if(front < end) {
        mergesort(front, (front+end)/2);
        mergesort((front+end)/2+1, end);
        merge(front, end, (front+end)/2);
    }
}

int main(void) {
    int n;
    while(scanf("%d", &n) && n) {
        ans = 0;
        for(int i=1; i<=n; ++i) {
            cin >> a[i];
        }
        mergesort(1, n);
        cout << ans << endl;
    }
    return 0;
}


树状数组做法:

如输入val:9 1 0 5 4

则这五个数对应下标依次为pos:1 2 3 4 5

把输入的五个数联合下标进行排序得到:

val:0 1 4 5 9

pos:3 2 5 4 1

再令r[pos[i]] == i

此时有:

下标:  1 4 5

val:   0 1 4 5 9

r:     1 2 3 4 5//此时val和r中元素相对大小关系相同

pos: 3 2 5 4

得到:

下标i==r[pos[i]]: 1 2 3 4 5

pos: 3 2 5 4 1


可以发现此时r数组中的数值相对大小关系和val数组中相对大小关系相同

但是数值之间变得更加紧凑!这就使得我们缩小了所使用数组的范围,节省了空间;这叫做离散化操作

接下来我们只要找到r数组的逆序数即可


顺序访问r并执行树状数组add(r[i], 1)操作

则执行树状数组sum(r[i])返回小于等于r[i]的数的个数

i表示当前数组中插入的数的个数

则i-sum(r[i])即为1-i这些下标存放数中大于r[i]的数个数

代码如下:

#include <cstdio>
#include <iostream>
#include <algorithm>
#define MAXN 500010
#define LL long long
using namespace std;

struct Node {
    int val, pos;
}node[MAXN];
int r[MAXN],c[MAXN], n;
LL ans;

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

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

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

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

int main(void) {
    while(scanf("%d", &n) && n) {
        ans = 0;
        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) {
            r[node[i].pos] = i;
            c[i] = 0;
        }
        for(int i=1; i<=n; ++i) {
            add(r[i]);
            printf("\n----------%d:\n", i);
            for(int j=1; j<=n; ++j)
                printf("%d\t", c[j]);
            printf("--------%d\n", i-sum(r[i]));
            ans += i-sum(r[i]);
        }
        cout << ans << endl;
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值