求逆序对最常用的方法就是树状数组了,确实,树状数组是非常优秀的一种算法。在做POJ2299时,接触到了这个算法,理解起来还是有一定难度的,那么下面我就总结一下思路:

首先:因为题目中a[i]可以到999,999,999之多,在运用树状数组操作的时候,用到的树状数组C[i]是建立在一个有点像位存储的数组的基础之上的,不是单纯的建立在输入数组之上。 
比如输入一个9 1 0 5 4(最大9)

那么C[i]树状数组的建立是在: 
下标 0 1 2 3 4 5 6 7 8 9 –——下标就要建立到9 
数组 1 1 0 0 1 1 0 0 0 1 –——通过1来表示存在 
现在由于999999999这个数字相对于500000这个数字来说是很大的,所以如果用数组位存储的话,那么需要999999999的空间来存储输入的数据。 这样是很浪费空间的,题目也是不允许的,所以这里想通过离散化操作。

那么怎么离散化操作呢?离散化是一种常用的技巧,有时数据范围太大,可以用来放缩到我们能处理的范围,必要的是建立一个结构体a[n],v表示输入的值,order表示原i值,再用一个数组aa[n]存储离散化后的值 
例如: 
          i:1 2 3 4 5 
         v:9 0 1 5 4 
     sort:0 1 4 5 9 
   order:2 3 5 4 1

       aa:5 1 2 4 3    //建立映射:aa[a[i].order]=i; 

即原本的9经过排序应该在第5位,现在aa[1]=5,对应原来的9,大小次序不变,只是将9缩小到了5

那么离散化之后怎么求逆序对呢?首先是通过update函数插入一个数,比如update(2,1),一开始都c[n]为0,插入后+1 
现在其余的为0,c[2],c[4]=1,这就说明前面下标为2处有一个数2,这里是关键,c[4]=1不代表下标为4时有一个数4,它的意思是在4之前的区间内所有元素之和是1,即有一个数2,具体的可以看看树状图 
然后只有用getsum实时求出插入一个数的前面有几个数,就可以算出当前小于等于这个数的数的个数,再通过下标i-getsum(aa[i]),得到大于它的数目,即为逆序数。 
上面样例的解释:

i:1 2 3 4 5

aa:5 1 2 4 3 

i=1->插入aa[1]

调用upDate(5, 1),把第5位设置为1 
1 2 3 4 5 
0 0 0 0 1 
计算1-5上小于等于5的数字存在么? 这里用到了树状数组的getSum(5) =1操作 
现在用输入的下标1 - getSum(5) = 0 就可以得到对于5的逆序数为0。

i=2-> 插入aa[2]

调用upDate(1, 1),把第1位设置为1 
1 2 3 4 5 
1 0 0 0 1 
注意这里实际输出的是11 0 1 1,但就像上面说的这里真正的含义是只插入1这个数

c[4],c[2]只是因为前面c[1]变化才变化的,而且变化了也不要紧,通过后面的getsum求的总是区间的和

后面计算时,只计算c[4]的值,前面是不加上去的,而c[4]的值就是前面到4所以出现的数的和 
计算1-1上小于等于1的数字存在么? 这里用到了树状数组的getSum(1)= 1操作
现在用输入的下标2 - getSum(2) = 1 就可以得到对于1的逆序数为1。

i=3-> 插入aa[3]

调用upDate(2, 1),把第2位设置为1 
1 2 3 4 5 
1 1 0 0 1

计算1-2上小于等于2的数字存在么? 这里用到了树状数组的getSum(2)= 2操作
现在用输入的下标3 - getSum(2) = 1 就可以得到对于2的逆序数为1。

i=4->插入aa[4]

调用upDate(4, 1),把第4位设置为1 
1 2 3 4 5 
1 1 0 1 1

计算1-4上小于等于4的数字存在么? 这里用到了树状数组的getSum(4)= 3操作
现在用输入的下标4 - getSum(2) = 1 就可以得到对于4的逆序数为1。

i=5-> 插入aa[5]

调用upDate(3, 1),把第3位设置为1 
1 2 3 4 5 
1 1 1 1 1

计算1-3上小于等于3的数字存在么? 这里用到了树状数组的getSum(3)= 3操作
现在用输入的下标5 - getSum(3) = 2 就可以得到对于2的逆序数为2。

总逆序数为上述逆序数之和,即0+1+1+1+2=5

POJ2299代码如下:

  1. #include <iostream>
  2. #include <stdio.h>
  3. #include <string.h>
  4. #include <stdlib.h>
  5. #include <algorithm>
  6. using namespace std;
  7.  
  8. //aa[] : 离散化后的数组
  9. //c[] : 树状数组
  10. const int maxn= ;
  11. int aa[maxn];
  12. int c[maxn];
  13. int n;
  14.  
  15. //v : value
  16. //order : 输入时的相对次序
  17. struct Node
  18. {
  19. int v;
  20. int order;
  21. }a[maxn];
  22.  
  23. bool cmp(Node a, Node b)
  24. {
  25. return a.v < b.v;
  26. }
  27.  
  28. int lowbit(int k)
  29. {
  30. return k&(-k);
  31. }
  32.  
  33. void update(int t, int value)
  34. {
  35.   //即一开始都为0,一个个往上加(+1)
  36. int i;
  37. for (i = t; i <= n; i += lowbit(i))
  38. c[i] += value;
  39. }
  40.  
  41. int getsum(int t)
  42. {
  43.   //即就是求和函数,求前面和多少就是小于它的个数
  44. int i, sum = ;
  45. for (i = t; i >= ; i -= lowbit(i))
  46. sum += c[i];
  47. return sum;
  48. }
  49.  
  50. int main()
  51. {
  52. int i;
  53. while (scanf("%d", &n), n)
  54. {
  55. //离散化
  56. for (i = ; i <= n; i++)
  57. {
  58. scanf("%d", &a[i].v);
  59. a[i].order = i;
  60. }
  61. sort(a + , a + n + ,cmp);
  62. memset(c, , sizeof(c));
  63. for (i = ; i <= n; i++)
  64. aa[a[i].order] = i;
  65. __int64 ans = ;
  66. for (i = ; i <= n; i++)
  67. {
  68. update(aa[i], );
  69. //减去<=的数即为大于的数,即为逆序数
  70. ans += i - getsum(aa[i]);
  71. }
  72. printf("%I64d\n", ans);
  73. }
  74. return ;
  75. }

POJ 2299树状数组求逆序对的更多相关文章

  1. poj 2299 树状数组求逆序对数+离散化

    Ultra-QuickSort Time Limit: 7000MS   Memory Limit: 65536K Total Submissions: 54883   Accepted: 20184 ...

  2. Ultra-QuickSort POJ - 2299 树状数组求逆序对

    In this problem, you have to analyze a particular sorting algorithm. The algorithm processes a seque ...

  3. poj 2299 树状数组求逆序数+离散化

    http://poj.org/problem?id=2299 最初做离散化的时候没太确定可是写完发现对的---由于后缀数组学的时候,,这样的思维习惯了吧 1.初始化as[i]=i:对as数组依照num ...

  4. POJ2299Ultra-QuickSort(归并排序 + 树状数组求逆序对)

    树状数组求逆序对   转载http://www.cnblogs.com/shenshuyang/archive/2012/07/14/2591859.html 转载: 树状数组,具体的说是 离散化+树 ...

  5. [NOIP2013提高&洛谷P1966]火柴排队 题解(树状数组求逆序对)

    [NOIP2013提高&洛谷P1966]火柴排队 Description 涵涵有两盒火柴,每盒装有 n 根火柴,每根火柴都有一个高度. 现在将每盒中的火柴各自排成一列, 同一列火柴的高度互不相 ...

  6. [NOI导刊2010提高&洛谷P1774]最接近神的人 题解(树状数组求逆序对)

    [NOI导刊2010提高&洛谷P1774]最接近神的人 Description 破解了符文之语,小FF开启了通往地下的道路.当他走到最底层时,发现正前方有一扇巨石门,门上雕刻着一幅古代人进行某 ...

  7. 【bzoj2789】[Poi2012]Letters 树状数组求逆序对

    题目描述 给出两个长度相同且由大写英文字母组成的字符串A.B,保证A和B中每种字母出现的次数相同. 现在每次可以交换A中相邻两个字符,求最少需要交换多少次可以使得A变成B. 输入 第一行一个正整数n ...

  8. “浪潮杯”第九届山东省ACM大学生程序设计竞赛(重现赛)E.sequence(树状数组求逆序对(划掉))

    传送门 E.sequence •题意 定义序列 p 中的 "good",只要 i 之前存在 pj < pi,那么,pi就是 "good": 求删除一个数, ...

  9. 2021.12.10 P5041 [HAOI2009]求回文串(树状数组求逆序对)

    2021.12.10 P5041 [HAOI2009]求回文串(树状数组求逆序对) https://www.luogu.com.cn/problem/P5041 题意: 给一个字符串 \(S\) ,每 ...

随机推荐

  1. border-color的深入理解

    .className{ width:100px;height:100px; border:100px solid; border-color: red green blue orange; } 最终的 ...

  2. Base64 总结

    Base64编码是解决一些无法打印的字符无法显示的问题,将8位的ascii编码转换为6位的表示64个可见字符的算法. 具体而言,首先将编码每三个分成一组,将三个字符转换为总长为24位的二进制 数字,将 ...

  3. 扫描某目录下的所有文件的MD5码并导出文件【可执行jar】

    pom <?xml version="1.0" encoding="UTF-8"?> <project xmlns="http:// ...

  4. linux缺失gcc的安装方法

    linux安装gcc操作 1.查看linux是否有gcc文件 这个是没有挂载的 2. 使用df,查看系统光盘的挂载位置 3.卸载分区 umount /dev/sr0 4.将redhat系统光盘重新载入 ...

  5. .net Json 反序列化时,属性带点

    .net Json 反序列化时,属性带点 使用[JsonProperty("xxx.xxx")] static void Main(string[] args) { string ...

  6. 前端 -- BOM和DOM

    一,引入 到目前为止,已经学过了JavaScript的一些简单的语法.但是这些简单的语法,并没有和浏览器有任何交互. 也就是我们还不能制作一些我们经常看到的网页的一些交互,我们需要继续学习BOM和DO ...

  7. 洛谷p1586四方定理题解

    题目 这个题的本质是动态规划中的背包问题. 为什么会想到背包呢. 因为往往方案数不是排列组合就是递推或者是dp,当然还有其他的可能.我们可以把一个数的代价当成这个数的平方,价值就是一个方案数.由于这个 ...

  8. 【AGC002E】Candy Piles 博弈论

    题目大意 有\(n\)堆糖果,第\(i\)堆有\(a_i\)个. 两个人轮流决策,决策分为两种: 1.选择糖果数最多的一堆糖果,并把这堆糖全吃了. 2.在每堆非空的糖果堆里拿一颗糖吃掉. 吃掉最后一颗 ...

  9. bzoj 3631 松鼠的新家 (树链剖分)

    链接: https://www.lydsy.com/JudgeOnline/problem.php?id=3631 思路: 直接用树链剖分求每一次运动,因为这道题只需要区间增添,单点求值,没必要用线段 ...

  10. VueCLI3如何更改安装时的包管理器为yarn或npm

    在执行 vue create project 后如果显示如下 npm run serve 则表示你使用的是npm创建的项目. 如果显示如下 yarn serve 则表示此项目为yarn创建. 那如何切 ...