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

来源:互联网 发布:同和行知职业技术学校 编辑:程序博客网 时间:2024/06/05 20:05

POJ2299 原题链接:http://poj.org/problem?id=2299

Ultra-QuickSort
Time Limit: 7000MS Memory Limit: 65536KTotal Submissions: 63588 Accepted: 23705

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

59105431230

Sample Output

60


原题大意:输入一串数据,求它的逆序数;

思路:暴力的方法肯定超时,用树状数组可以将复杂度降到O(nlogn);

这道题大体的思路为:

1.开一个能大小为这些数的最大值的树状数组,并全部置0;

2.从头到尾读入这些数,每读入一个数就更新树状数组,查看它前面比它小的已出现过的有多少个数sum;

3.用当前位置减去该sum,就可以得到当前数导致的逆序对数了。把所有的加起来就是总的逆序对数;


但是,题目给的一个数的范围最大为999,999,999显然我们的树状数组不能开到那么大;这里要用到离散化,就是把输入的所有数离散化到1~n的范围内在进行1~3步的操作;


离散化过程:

1.创建一个结构体

const int N=5e5+10;typedef struct{    int val;   //输入值    int pos;   //原下标}NODE;NODE node[N];
在创建一个数组a[N];

把node[N]按照val的值从小到大排列之后令 a[node[i].pos]=i (i=1,2,3,4,5,6...n)

过程如下:

1.拿题目第一个样例,输入的为 val: 9 1 0 5 4;

                             其原下标为pos: 1 2 3 4 5;

                                   node[i]的   i: 1 2 3 4 5;

2.排序之后  val: 0 1 4 5 9                             

                  pos: 3 9 5 4 1                            

                           i: 1 2 3 4 5                                


3.执行a[node[i].pos]=i (i=1,2,3,4,5,6...n)后:

                  i: 1 2 3 4 5;

             a[i]: 5 2 1 4 3;

   pos[i].val: 9 1 0 5 4;



如上所示,这样做就将1~9缩小为了1~5且大小关系不变的存储在了a[i]中;这时我们可以忽视掉原数组了,因为大小关系不变的话逆序数的对数也不会改变;

那么问题来了,这道题该如何使用树状数组求解呢?

由于a[i]中储存的数是按照原输入的大小关系存储的,那么我们可以:.从头到尾读入这些数,每读入一个数就更新树状数组,查看它前面比它小的已出现过的有多少个数sum;

还是拿样例:

  1. 如果数据不是很大, 可以一个个插入到树状数组中,  
  2.     每插入一个数, 统计比他小的数的个数,  
  3.     对应的逆序为 i- sum( a[i] ),  
  4.     其中 i 为当前已经插入的数的个数,  
  5.     sum( a[i] )为比 a[i] 小的数的个数,  
  6.     i- sum( a[i] ) 即比 a[i] 大的个数, 即逆序的个数  
  7.     但如果数据比较大,就必须采用离散化方法  
  8.     假设输入的数组是9 1 0 5 4, 离散后的结果a[] = {5,2,1,4,3};  
  9. 在离散结果中间结果的基础上,那么其计算逆序数的过程是这么一个过程。  
  10. 1.输入5,   调用add(5, 1),把第5位设置为1  
  11. 1 2 3 4 5  
  12. 0 0 0 0 1  
  13. 计算1-5上比5小的数字存在么? 这里用到了树状数组的sum(5) = 1操作,  
  14. 现在用输入的下标1 -sum(5) = 0 就可以得到对于5的逆序数为0。  
  15. 2. 输入2, 调用add(2, 1),把第2位设置为1  
  16. 1 2 3 4 5  
  17. 0 1 0 0 1  
  18. 计算1-2上比2小的数字存在么? 这里用到了树状数组的sum(2) = 1操作,  
  19. 现在用输入的下标2 - sum(2) = 1 就可以得到对于2的逆序数为1。  
  20. 3. 输入1, 调用add(1, 1),把第1位设置为1  
  21. 1 2 3 4 5  
  22. 1 1 0 0 1  
  23. 计算1-1上比1小的数字存在么? 这里用到了树状数组的sum(1) = 1操作,  
  24. 现在用输入的下标 3 -sum(1) = 2 就可以得到对于1的逆序数为2。  
  25. 4. 输入4, 调用add(4, 1),把第5位设置为1  
  26. 1 2 3 4 5  
  27. 1 1 0 1 1  
  28. 计算1-4上比4小的数字存在么? 这里用到了树状数组的sum(4) = 3操作,  
  29. 现在用输入的下标4 - sum(4) = 1 就可以得到对于4的逆序数为1。  
  30. 5. 输入3, 调用add(3, 1),把第3位设置为1  
  31. 1 2 3 4 5  
  32. 1 1 1 1 1  
  33. 计算1-3上比3小的数字存在么? 这里用到了树状数组的sum(3) = 3操作,  
  34. 现在用输入的下标5 - sum(3) = 2 就可以得到对于3的逆序数为2。  
  35. 6. 0+1+2+1+2 = 6 这就是最后的逆序数  
  36. 分析一下时间复杂度,首先用到快速排序,时间复杂度为O(NlogN),  
  37. 后面是循环插入每一个数字,每次插入一个数字,分别调用一次add()和sum()  
  38. 外循环N, add()和sum()时间O(logN) => 时间复杂度还是O(NlogN)  


   以上摘自:http://blog.csdn.net/guhaiteng/article/details/52138756(自己还是太菜,无法用自己的语言描述出来


以下为我的ac代码:

#include <iostream>#include <cstdio>#include <algorithm>using  namespace std;const int N=5e5+10;typedef struct{    int val;    int pos;}NODE;NODE node[N];int n;int a[N];int tree[N];bool cmp(const NODE& a, const NODE& b){  return a.val<b.val;}void add(int i){    while(i<=n)    {        tree[i]+=1;        i=i+(i&-i);    }}int sum(int i){    int s=0;    while(i>0)    {        s=s+tree[i];        i=i-(i&-i);    }    return s;}int main(){    while((scanf("%d",&n))!=EOF)    {        if(n==0) break;        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++)            a[node[i].pos]=i;    //离散化        for(int i=1;i<=n;i++)            tree[i]=0;    //初始化树状数组        long long ans=0;        for(int i=1;i<=n;i++)        {            add(a[i]);            ans+=i-sum(a[i]);        }        cout<<ans<<endl;    }}




阅读全文
0 0
原创粉丝点击