標籤:設定 例子 hide 表示 logs 求和 逆序數 要求 lowbit
1、什麼是逆序數?2、用樹狀數組求逆序數的總數 2.1該背景下樹狀數組的含義 2.2如何使用樹狀數組求逆序數總數 2.3 C++實現代碼
View Code
1、什麼是逆序數?
在一個排列中,如果一對數的前後位置與大小順序相反,即前面的數大於後面的數,那麼它們就稱為一個逆序。一個排列中逆序數的總數就是這個排列的逆序數。
2、用樹狀數組求逆序數的總數
2.1該背景下樹狀數組的含義
我們假設一個數組A[n],當A[n]=0時表示數字n在序列中沒有出現過,A[n]=1表示數字n在序列中出現過。A對應的樹狀數組為c[n],則c[n]對應維護的是數組A[n]的內容,即樹狀數組c可用於求A中某個區間的值的和。
樹狀數組的插入函數(假設為 void insert(int i,int x) )的含義:在求逆序數這個問題中,我們的插入函數通常使用為insert( i , 1 ),即將數組A[i]的值加1 (A數組開始應該初始化為0,所以也可以理解為設定A[ i ]的值為1,即將數字i 加入到序列的意思 )。,同時維護c數組的值。
樹狀數組中區間求和函數(假設函數定義為: int getsun(int i ) )的含義:該函數的作用是用於求序列中小於等於數字 i 的元素的個數。這個是顯而易見的,因為樹狀數組c 維護的是數組A的值,則該求和函數即是用於求下標小於等於 i 的數組A的和,而數組A中元素的值要麼是0要麼是1,所以最後求出來的就是小於等於i的元素的個數。
所以要求序列中比元素a大的數的個數,可以用i - getsum(a)即可( i 表示此時序列中元素的個數)。
2.2如何使用樹狀數組求逆序數總數
首先來看如何減小問題的規模:
要想求一個序列 a b c d,的逆序數的個數,可以理解為先求出a b c的逆序數的個數k1,再在這個序列後面增加一個數d,求d之前的那個序列中值小於d的元素的個數k2,則k1+k2即為序列a b c d的逆序數的個數。
舉個例子加以說明:
假設給定的序列為 4 3 2 1,我們從左往右依次將給定的序列輸入,每次輸入一個數temp時,就將當前序列中大於temp的元素的個數計算出來,並累加到ans中,最後ans就是這個序列的逆序數個數。
序列的變化(底線為新增加元素) |
序列中大於新增加的數位個數 |
操作 |
{ } |
0 |
初始化時序列中一個數都沒有 |
{4 } |
0 |
往序列中增加4,統計此時序列中大於4的元素個數 |
{4 3 } |
1 |
往序列中增加3,統計此時序列中大於3的元素個數 |
{4 3 2} |
2 |
往序列中增加2,統計此時序列中大於2的元素個數 |
{4 3 2 1} |
3 |
往序列中增加1,統計此時序列中大於1的元素個數 |
當所有的元素都插入到序列後,即可得到序列{4 3 2 1}的逆序數的個數為1+2+3=6.
2.3 C++實現代碼如下:
#include <iostream>#include <string>using namespace std;#define N 1010int c[N]; int n;int lowbit(int i){ return i&(-i);}int insert(int i,int x){ while(i<=n){ c[i]+=x; i+=lowbit(i); } return 0;}int getsum(int i){ int sum=0; while(i>0){ sum+=c[i]; i-=lowbit(i); } return sum;}void output(){ for(int i=1;i<=n;i++) cout<<c[i]<<" "; cout<<endl;}int main(){ while(cin>>n){ int ans=0; memset(c,0,sizeof(c)); for(int i=1;i<=n;i++){ int a; cin>>a; insert(a,1); ans+=i-insert(a);//統計當前序列中大於a的元素的個數 } cout<<ans<<endl; } return 0;}
——來自 熊貓 [http://www.cnblogs.com/xiongmao-cpp/]
樹狀數組求逆序對