樹狀陣列求逆序對及離散化
逆序對指的是乙個序列中有兩個數ai和aj,iaj,即它們下標與數值的增減不一致,那麼對於這個問題:求乙個序列中逆序對的個數,該如何解決呢?
我最初接觸到的方法是歸併排序,是個很不錯的方法,但是對於向我一樣的蒟蒻……還是有理解難度,而今天講的樹狀陣列解法,至少……理解難度降低了不少。
樹狀陣列求逆序的思想事實上和樹狀陣列關係不大,以下圖為例(自己畫的,醜:):
如上圖,第一次將第乙個數1對應的a[1]++,這時還看不出來,再將4對應的a[4]++,同理a[2]++……即將n對應的a[n]++,這樣做,就可以將乙個無序的序列變得有序,同時a陣列也表示了對應下標的數是否出現/處理過,而且當只有i個元素變換之時,剩餘元素不會對接下來的操作造成影響,現在給出計算到2時的:
那麼,對於最新處理的2或任意乙個n(設下標為i)來說,前i個數中,比它小及其本身的數的個數,就是前i項的字首和,因為排在原序列中i之後的數尚未處理,而已處理的a中比他小的數必然在它前面,且對應a值為1,那麼,已處理的i個數中,比這個數n大的數的個數,也就是這乙個數的逆序對數,就是i-getsum(n),而字首和的求值與a陣列的修改、維護,就可以交給樹狀陣列了:
int lowbit(int a)
void add(int p,int c)
}int getsum(int p)
return ans;
}
函式並沒有變化,只是主函式中的呼叫變成了這樣:
add(a[i], 1);
ans += i - getsum(a[i]);
列舉一下i就可以了
但是,這個問題還有大坑!!我們的插入,是基於修改n對應的a[n]的,但是,如果n達到了2^31級?你開的下這麼大的陣列嗎?
這個時候,就是離散化出場的時候了,離散化作為一種常見的優化方式,其實原理很簡單,用乙個結構體將數的值和下標聯絡在一起,再按數值排一次序,將i賦給loc[a[i].pos]//pos為下標//,這樣,就將資料範圍壓縮在了序列長度以內,極大的壓縮了空間:
struct node
a[100000]
bool cmp(node a,node b)
{ if(a.val==b.val)return a.pos<=b.pos;
else return a.val比如100 1 5 20 2 離散化為5 1 3 4 2,極大的節省了空間。
樹狀陣列求逆序對,說到底,就是對求和函式的特殊運用,光會求逆序對,作用不大,關鍵是要明白怎樣根據題目要求,去處理序列,求和到底可以幹什麼?這些都需要經驗積累,推薦幾道題:洛谷p1908 p1521 poj2299
poj 3067 火柴排隊 奶牛集會,謝謝。
樹狀陣列 (離散化 樹狀陣列 求逆序對)
sample test s input 52 3 1 5 4 output 3 題目大意 求逆序對的個數 題目分析 求逆序對有很多方法,比如說用合併排序 分治 樹狀陣列 線段樹,甚至連暴力 氣泡排序 也可以做,但是要注意會不會超時。這裡就講一下樹狀陣列的方法,這一題最有意思的是離散化的方法,這個方法...
樹狀陣列求逆序對 離散化 poj2299
今天做了乙個樹狀陣列求逆序對的題,需要離散化,看了部落格明白了為什麼要進行離散化,原因是樹狀陣列中的c maxn 陣列其實相當於乙個雜湊操作,如果所給陣列中存在值特別大的元素,陣列就需要離散化 include include include include using namespace std s...
樹狀陣列 模板3 求逆序對(非離散化)
離散化的樹狀陣列 這裡就提一下和普通樹狀陣列的區別,這裡是用要查詢的數當作下標,而普通樹狀陣列是直接順序下標放值,也就是說區別在於 updata函式,普通的是updata i,k 這裡的i就單純是區間下標 而求逆序對的時候是updata a i 1 我們每次都是在這個數字大小的位置上新增1,然後去更...