樹狀數組求逆序數的原理


       求逆序數的方法有很多,比如歸並排序,但本文重點講一下如何用樹狀數組來求逆序數。

       當數據的范圍較小時,比如maxn=100000,那么我們可以開一個數組c[maxn],來記錄前面數據的出現情況,初始化為0;當數據a出現時,就令c[a]=1。這樣的話,     欲求某個數a的逆序數,只需要算出在當前狀態下c[a+1,maxn]中有多少個1,因為這些位置的數在a之前出現且比a大。但是若每添加一個數據a時,就得從a+1到      maxn搜一遍,復雜度太高了。樹狀數組卻能很好的解決這個問題,同樣開一個數組d[maxn],初始化為0,d[i]記錄下i結點所管轄范圍內當前狀態有多少個數;當添加數     據a時,就向上更新d,這樣一來,欲求a的逆序數時,只需要算sum(maxn)-sum(a);sum(i)表示第i個位置之前出現了多少個1. 

   舉個例子:有5個數,分別為5 3 4 2 1,當讀入數據a=5時,c為:0,0,0,0,1;d為:0,0,0,0,1;當讀入數據a=3時,c為:0,0,1,0,1;d為:0,0    1,1,1;當讀入數據a=4時,c為:0,0,1,1,1;d為:0,0,1,2,1;…………。

       此思想的關鍵在於,讀入數據的最大值為maxn,由於maxn較小,所以可以用數組來記錄狀態。當maxn較大時,直接開數組顯然是不行了,這是的解決辦法就是離散   化。所謂離散化,就是將連續問題的解用一組離散要素來表征而近似求解的方法,這個定義太抽象了,還是舉個例子吧。

   假如現在有一些數:1234 98756 123456 99999 56782,由於1234是第一小的數,所以num[1]=1;依此,有num[5]=2,num[2]=3,num[4]=4,num[3]=5;這     樣轉化后並不影響原來數據的相對大小關系,何樂而不為呢!!!

       還有一點值得注意,當有數據0出現時,由於0&0=0,無法更新,此時我們可以采取加一個數的方法將所有的數據都變成大於0的。

      下面看代碼:

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<cmath>
 4 #include<algorithm>
 5 #define ll long long
 6 #define maxn 100005
 7 using namespace std;
 8 int c[maxn],n;
 9 int low_bit(int i)
10 {
11     return i&(-i);
12 }
13 void update(int i,int v)
14 {
15     while(i<=n){
16         c[i]+=v;
17         i+=low_bit(i);
18     }
19 }
20 int get_sum(int i)
21 {
22     int res=0;
23     while(i){
24         res+=c[i];
25         i-=low_bit(i);
26     }
27     return res;
28 }
29 int main()
30 {
31     while(scanf("%d",&n),n)
32     {
33         memset(c,0,sizeof(c));
34         int ans=0;
35         for(int i=1;i<=n;i++)
36         {
37             int a;
38             scanf("%d",&a);
39             update(a,1);
40             ans+=i-get_sum(a);
41         }
42         printf("%d\n",ans);
43     }
44     return 0;
45 }
View Code

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM