1. 程式人生 > >POJ 2299 Ultra-QuickSort(樹狀數組 + 離散)

POJ 2299 Ultra-QuickSort(樹狀數組 + 離散)

冒泡 我們 for 輸入 turn 就是 pda 大小 有用

鏈接: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

   

   

POJ 2299 Ultra-QuickSort(樹狀數組 + 離散)