链接: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 }
原文地址:https://www.cnblogs.com/doublebit/p/10836072.html
时间: 2024-11-10 09:38:33