逆序數就是數中各位在它前面有多少個數比它大,求出這些元素個數之和。
今天看了個樹狀陣列,可以很好的解決這個問題,普通方法需要o(n^2)複雜度,用樹狀陣列只需要o(nlongn)
樹狀陣列實際上還是乙個陣列,只不過它的每個元素儲存了跟原來陣列的一些元素相關的結合值。
若a為原陣列,定義陣列c為樹狀陣列。c陣列中元素c[ i ]表示a[ i –lowbit( i ) + 1]至a[ i ]的結合值。
lowbit(i)是i的二進位制中最後乙個不為零的位數的2次方,可以這樣計算
lowbit(i)=x&(-x)
lowbit(i)=x&(x^(x-1))
當想要查詢乙個sum(n)時,可以依據如下演算法即可:
step1: 令sum = 0,轉第二步;
step2: 假如n <= 0,演算法結束,返回sum值,否則sum = sum + cn,轉第三步;
step3: 令n = n – lowbit(n),轉第二步。
n = n – lowbit(n)這一步實際上等價於將n的二進位制的最後乙個1減去。而n的二進位制裡最多有log(n)個1,所以查詢效率是log(n)的。
修改乙個節點,必須修改其所有祖先,最壞情況下為修改第乙個元素,最多有log(n)的祖先。所以修改演算法如下(給某個結點i加上x):
step1: 當i > n時,演算法結束,否則轉第二步;
step2: ci = ci + x, i = i + lowbit(i)轉第一步。
i = i +lowbit(i)這個過程實際上也只是乙個把末尾1補為0的過程。
求逆序的思路:
可以把數乙個個插入到樹狀陣列中, 每插入乙個數, 統計比他小的數的個數,對應的逆序為 i- getsum( data[i] ),其中 i 為當前已經插入的數的個數, getsum( data[i] )為比 data[i] 小的數的個數,i- getsum( data[i] ) 即比 data[i] 大的個數, 即逆序的個數。最後需要把所有逆序數求和,就是在插入的過程中邊插入邊求和。
下面是**:
[cpp]view plain
copy
#include
using
namespace std;
#define n 10
struct node;
node d[n+1];
int inverse[n+1];
int count[n];
int cmp(
const
void*a,const
void*b)
int lowbit(
int t)
void modify(
int pos,int num)
} int sum(
int end)
return sum;
} int main()
qsort(d+1,n,sizeof(node),cmp);
int id=1;
count[d[1].pos]=1;
for(
int i=2;i<=n;i++)
int num=0;
for(
int i=1;i<=n;i++)
cout<
return 0;
}
中間用到了排序,需要統計位於下標i處比i小的數,然後在樹狀陣列中計算每個位置的和。
排序複雜度o(nlogn), 計算逆序數和的時候也是o(nlogn).
這裡處理的是乙個數的不同位,當然可以擴充套件到很多數。
樹狀陣列求逆序數
chikachika說希望和我一起做學園偶像的時候,我真的很開心。watanabeyouwatanabeyou 曜是千歌的青梅竹馬,但是aqoursaqours成立以後,千歌似乎總是與梨子在一起,而把曜冷落了。為了讓千歌知曉自己的心意,曜醬決定做一件大事!她決定把乙個給定的11 nn的排列 1 ai...
樹狀陣列 求逆序數
一.樹狀陣列介紹 1 性質 樹狀陣列本質上就是乙個陣列,它與普通陣列不同之處在於它的某些元素維護的是一段區間的資訊,已區間和為例,若i為奇數,則第i個元素就是源資料的第i個元素,若i為偶數,則第i個元素維護的是 i 2 k 1,i 這段區間的和,k代表i的二進位制末尾0的個數。2 作用 樹狀陣列常用...
樹狀陣列求逆序數
1 什麼是逆序數?在乙個排列中,如果一對數的前後位置與大小順序相反,即前面的數大於後面的數,那麼它們就稱為乙個逆序。乙個排列中逆序數的總數就是這個排列的逆序數。2 用樹狀陣列求逆序數的總數 2.1該背景下樹狀陣列的含義 我們假設乙個陣列a n 當a n 0時表示數字n在序列中沒有出現過,a n 1表...