POJ 2299 Ultra-QuickSort(树状数组 + 离散)

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

题意:给出N个数组成的数列A(0 <= A[i] <= 999,999,999),求该数列逆序对的数量。

分析:题目所谓的排序过程其实就是一个冒泡排序的过程。在这里,我们需要知道,冒泡排序所需交换的次数等于该序列逆序对的数量(证明略)。这是这道题的一个切入点。

   树状数组可以很方便地求出数列的前缀和,对于一个数x,我们使树状数组上第x个元素的值赋为1,这时调用Sum(x)就可以得到一个从第1项到第x项的前缀和。这意味着我们可以通过这种方法来知道到目前为止出现了多少个比x小的数(其个数即为Sum(x))。由于题目对排序的要求是升序,因此我们真正要找的其实是到目前为止出现了多少个比x大的数,所以逆序对的个数应为【当前插入到树状数组中的元素个数】 - Sum(x)。因此,树状数组可以很好的解决我们现在这个问题。

   从上面我们得知,我们是通过树状数组来得到前x项和进而求得我们所要得结果的。但是本题的x范围太大,运行环境不允许开这么大的数组。从题面得知输入的数不超过500000,遇到这种局面,不难想到要使用离散化去减轻空间上的负担。

   事实上,数列上的数值本身其实对我们是没有用的。因为我们要求的是逆序对,真正对我们有价值的是数与数之间的大小关系,我们额外保存数列中元素的位置后,对数列进行一次排序,便可将原数列的元素映射在1 ~ N(N为该数列的大小,N <= 500000)的区间里。这时我们再使用上述方法去求得逆序对的个数,就可以解决这个问题了。

 1 #include <cstdio>
 2 #include <cstring>
 3 #include <algorithm>
 4 
 5 using namespace std;
 6 
 7 const int MAXN = 500010;
 8 
 9 struct Node {
10     int val,id;
11     
12     bool operator < (const Node &p) const {
13         return val < p.val; 
14     }
15 }num[MAXN];
16 
17 int n;
18 int disp_num[MAXN];
19 int tree[MAXN];
20 
21 int lowbit(int x) {
22     return (x) & (-x);
23 }
24 
25 void update(int i,int val) {
26     while (i <= n) {
27         tree[i] += val;
28         i += lowbit(i);
29     }
30 }
31 
32 long long getSum(int i) {
33     long long ans = 0;
34     while (i > 0) {
35         ans += tree[i];
36         i -= lowbit(i);
37     }
38     return ans;
39 }
40 
41 int main() {
42     while (scanf("%d",&n),n) {
43         for (int i=1;i<=n;i++) {
44             scanf("%d",&num[i].val);
45             num[i].id = i;
46         }
47         sort(num+1,num+n+1);
48         for (int i=1;i<=n;i++) {
49             disp_num[num[i].id] = i;
50         }
51         long long ans = 0;
52         memset(tree,0,sizeof(tree));
53         for (int i=1;i<=n;i++) {
54             update(disp_num[i],1);
55             ans += i-getSum(disp_num[i]);
56         }
57         printf("%lld\n",ans);
58     }
59     
60     return 0;
61 }
View Code

   

   

猜你喜欢

转载自www.cnblogs.com/doublebit/p/10836072.html