(POJ 2299)Ultra-QuickSort 树状数组求逆序对数 + 离散化

Ultra-QuickSort
Time Limit: 7000MS Memory Limit: 65536K
Total Submissions: 60461 Accepted: 22417
Description

In this problem, you have to analyze a particular sorting algorithm. The algorithm processes a sequence of n distinct integers by swapping two adjacent sequence elements until the sequence is sorted in ascending order. For the input sequence
9 1 0 5 4 ,

Ultra-QuickSort produces the output
0 1 4 5 9 .

Your task is to determine how many swap operations Ultra-QuickSort needs to perform in order to sort a given input sequence.
Input

The input contains several test cases. Every test case begins with a line that contains a single integer n < 500,000 – the length of the input sequence. Each of the the following n lines contains a single integer 0 ≤ a[i] ≤ 999,999,999, the i-th input sequence element. Input is terminated by a sequence of length n = 0. This sequence must not be processed.
Output

For every input sequence, your program prints a single line containing an integer number op, the minimum number of swap operations necessary to sort the given input sequence.
Sample Input

5
9
1
0
5
4
3
1
2
3
0
Sample Output

6
0
Source

Waterloo local 2005.02.05

题意:
给你一个大小为n的数组,其中每个数各不相同;每次你可以交换相邻的两个数的位置,问你最少需要多少步,可以使得数组变成单调递增的。

分析:
首先,对于某一个位置的数而言,若前面已经为单调递增的有序数列了,那么要使这个位置的数也变得有序,那么在它前面有多少个数比他大他就要向前交换几次从而使得前面的整体是有序的。

那么前面的数到底如何才能满足“已经单调有序”这个条件呢?
我们来模拟一下:
首先第一个数来了,那么前面比他大的数的个数为0,所以不需要交换就可以满足单调有序了
当第二个数来的时候,若前面第一个数比他大,那么他就交换一次使得整体满足单调有序;否则不交换
当第三 个数来的时候。。。。。
以此类推
当第n个数来的时候,由于前面已经是单调有序的数列了,若前面有m个数比他大,那么向前交换m次就可以使得整体单调有序了

所以这一题的最后结果就是看每个位置上的数,前面有多少个比他大的数,然后加起来就可以了。其实就是求数列的逆序对数

接下来就是求逆序对数了:
我们可以利用树状数组来求一个数列的逆序对数
我们按照题目所给的数的顺序进行c[i]的更新(位置i表示数i出现),那么我们可以很快的求出比每个位置上的数小的且在前面的数的个数m了,然后用“位置”减去m就是前面比他大的数的个数了。

可是题目的数的范围太大了,没法开那么打的空间,但是数的数目只有500000个,所以我们就想到了“离散化”的思想,将所有的数按照他们的大小映射到1~n这n个数上(第一小的数映射为1。。。),然后在利用树状数组就可以了

离散化思路:

struct node
{
    int val,pos;
}nodes[maxn];

val 为原先数的值,pos 为数的位置 nodes[i].pos = i

然后我们将nodes[]按照val 值升序排列,排列后每个数的下标就是这个数的相对大小,也就是他在1~n中所对应的数了。

AC代码:

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

const int maxn = 500010;

struct node
{
    int val,pos;
}nodes[maxn];
int ans[maxn];
int n,c[maxn];

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

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

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

int cmp(node a,node b)
{
    return a.val < b.val;
}

int main()
{
    while(scanf("%d",&n)!=EOF && n)
    {
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&nodes[i].val);
            nodes[i].pos = i;
        }
        sort(nodes+1,nodes+n+1,cmp);
        for(int i=1;i<=n;i++) ans[nodes[i].pos] = i;
        for(int i=0;i<=n;i++) c[i] = 0;
        long long ansnum = 0;
        for(int i=1;i<=n;i++)
        {
            update(ans[i]);
            ansnum += i - getsum(ans[i]);
        }
        printf("%lld\n",ansnum);
    }
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值