POJ2299 Ultra-QuickSort(树状数组+离散化)

题目传送门

Ultra-QuickSort

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 9 \space 1 \space 0 \space 5 \space 4 9 1 0 5 4
Ultra-QuickSort produces the output
0   1   4   5   9 0\space 1\space 4\space 5\space 9 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

题意

输入一个数字n,然后输入n个数,求逆序数( n < 500000 , 0 ≤ a [ i ] ≤ 999999999 n < 500000,0 ≤ a[i] ≤ 999999999 n<5000000a[i]999999999

思路

很明显数字是不连续的,需要离散化处理,具体实现是先按照数字大小升序排序,修改每个数字为1-n的数,之后再按输入的顺序顺序排序,比如输入序列为9、1、0、5、4,处理之后是5、2、1、4、3

然后使用树状数组求逆序数即可

我们之前的 n 2 n^2 n2暴力统计是遍历当前数字x前面的数字,统计比x大的数字,那么树状数组是sum(n)- sum(x)(sum表示前缀和),每读入一个数字,就在每个数的位置加1,利用树状数组快速求前缀和,统计逆序数。举个例子:设输入序列为5、2、1、4、3 (n=5,共5个数字),求解过程如下所示:

  1. 则读入5时,在5的位置加1,表示5出现了一次

    数组 0 0 0 0 1

    下标 1 2 3 4 5

    此时逆序数为0,输入序列中,5前面没有数字

    sum(5)- sum(5)= 1-1=0

  2. 读入2,在2的位置加1

    那么树状数组只需要统计 (2,n] 这个区间的和就可以

    因为我们是一边读一遍更新数组,2之后的数组区间和就是在2前面出现的,比2大的数字的个数,因为读入一个数字我们就给对应位置加1,所以数组区间和就是数字的个数!

    数组 0 1 0 0 1

    下标 1 2 3 4 5

    用树状数组的就表示为 sum(5)- sum(2)= 2-1=1

    此时逆序数为0+1=1

  3. 读入1,1的位置加1,统计 (1,n] 这个区间的和

    数组 1 1 0 0 1

    下标 1 2 3 4 5

    用树状数组的就表示为 sum(5)- sum(1)= 3-1=2

    此时逆序数为0+1+2=3

  4. 读入4,4的位置加1,同样统计 (4,n] 这个区间的和

    数组 1 1 0 1 1

    下标 1 2 3 4 5

    用树状数组的就表示为 sum(5)- sum(4)= 4-3=1

    此时逆序数为0+1+2+1=4

  5. 读入3,3的位置加1,同样统计 (3,n] 这个区间的和

    数组 1 1 1 1 1

    下标 1 2 3 4 5

    用树状数组的就表示为 sum(5)- sum(3)= 5-3=2

    此时逆序数为0+1+2+1+2=6

于是我们就这样求出了逆序数!复杂度为nlogn

树状数组讲解:树状数组 数据结构详解与模板(可能是最详细的了)

代码

//#include 
#include 
#include 
#include 
#define MAXN 501000
using namespace std;
typedef long long ll ;
int tree[MAXN];
int lowbit(int x){
    return (-x)&x;
}
int add(int x,int val){
    while (x<MAXN){
        tree[x]+=val;
        x+=lowbit(x);
    }
}
ll sum(int x){
    ll res=0;
    while (x>0){
        res+=tree[x];
        x-=lowbit(x);
    }
    return res;
}
struct node{
    int num;
    int pos;
}arr[MAXN];
bool cmp(node a,node b){
    return a.num<b.num;
}
bool cmp2(node a,node b ){
    return a.pos<b.pos;
}
void init(){
    memset(tree,0,sizeof(tree));
}
int main (){
    int n;
    while(scanf("%d",&n)!=EOF){
        if (!n)break;
        init();
        ll ans=0;
        for (int i=1;i<=n;i++){
            scanf("%d",&arr[i].num);
            arr[i].pos=i;
        }
        sort(arr+1,arr+n+1,cmp);

        for (int i=1;i<=n;i++){
            arr[i].num=i;
        }
        sort(arr+1,arr+n+1,cmp2);
        for (int i=1;i<=n;i++){
            add(arr[i].num,1);
            ans+=sum(n)-sum(arr[i].num);
        }
        printf("%lld\n",ans);
    }
    return 0;
}

你可能感兴趣的:(ACM训练)