求逆序數的方法有很多,比如歸並排序,但本文重點講一下如何用樹狀數組來求逆序數。
當數據的范圍較小時,比如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 }