樹狀陣列求逆序對 POJ 2299 3067

2022-03-13 00:12:53 字數 2964 閱讀 2096

前幾天開始看樹狀陣列了,然後開始找題來刷。

首先是 poj 2299 ultra-quicksort: 

這題是指給你乙個無序序列,只能交換相鄰的兩數使它有序,要你求出交換的次數。實質上就是求逆序對,網上有很多人說它的原理是氣泡排序,可以用歸併排序來求出,但我一時間想不出它是如何和歸併排序搭上邊的(當初排序沒學好啊~),只好用剛學過的樹狀陣列來解決了。在poj 1990中學到了如何在實際中應用上樹狀陣列,沒錯,就是用個特殊的陣列來記錄即可,然後區間查詢和單點更新的複雜度都在o(logn)範圍內。

對於這道題,即處理到第i個數時,可以通過計算 i-1-它前面比它小的數得出前面比它大的數,即該數前面的逆序數;而統計某數前面比它小的數就是樹狀陣列的裸題了。分析到這裡已經差不多了,但是一看,a[i] ≤ 999,999,999,就不能簡單地用陣列下標來記錄a[i]的值了,這時我想起了之前看過的乙個很高大上的名字:"離散化",沒錯,就是離散化,因為n < 500,000而已,用不著那麼多陣列的空間,把a[i]通過排序再放進陣列即可,不過具體的題我還沒做過,對於這道題,我的做法是設定乙個輔助陣列 r[i] 表示第i個數的下標,初始化時也就是r[i]= i,然後通過比較對應a[i]的a[j]的值對r

排序。說白了就是對陣列a的下標進行排序(利用了劉汝佳小白書上

kruskal的做法)

,這時候

陣列r的逆序數就是陣列a

的逆序數。這是因為在排序時是一一對應的,每交換兩個數使a消除乙個逆序對時r便增加乙個逆序對(r本來是1,2,3,4,5……的順序數列),所以就轉化為了求陣列r的逆序對,此時下標只受n<500,000約束,開陣列不成問題,詳見**:

1 #include2 #include3 #include4

using

namespace

std;

5 typedef long

long

ll;6

const ll maxn= 500008;7

8 inline int lowbit(int x) 910

struct

treearray

13void clear()

14int sum(int x) const

20return

ans;21}

22void add(int x, int

d)27}28

} num(maxn);

2930

inta[maxn], r[maxn];

31bool cmp(int i, int j)

3233

intmain()

40 sort(r+1,r+n+1

,cmp);

41 ll inv= 0;42

num.clear();

43 num.n=n;

44for(i=1; i<=n; ++i)

48 printf("

%lld\n

",inv);49}

50return0;

51 }

樹狀陣列的寫法我參照了網上的人的寫法,把它封裝成乙個結構體,用於實現區間查詢和單點更新的陣列c放在了結構體內,這樣子可使**更清晰,不至於要開個全域性陣列容易混淆,如同矩陣快速冪一樣把矩陣的乘法過載在結構體中,可以避免在一些細節上浪費精力,**量也沒有增加多少。

然後是第二道 poj 3067: 

這題主要是說東西兩邊各有1~n和1~m個城市,然後有k條道路連線東西兩邊的城市,要求這k條邊的所有交點。這裡有個不大卻很易栽的坑:k的範圍沒給出,所以可以猜想其為n*m的上限,然後它給出的道路也沒說是有序的(千萬別被樣例騙了),所以我們首先要對它進行預處理才行。

如上題一樣,可以利用求逆序對的做法來求。為什麼呢?我們可以先按e(就是左邊的座標)排公升序,e相同的話按w(右邊的座標)排公升序(w一定要為公升序,因為e相同時的邊是不會有交叉點的,所以w公升序在統計時可以使e相同而w不同的邊不存在逆序對,符合沒有交叉點的實際情況),大體的預處理就是這樣,然後對排好序的w邊進行求逆序數即可,具體求法如上題所述。和上題不同的是,這裡的w值可以和前面的重複,但也不影響樹狀陣列的運用,要處理好細節,記住先返回區間的值即sum(x),再去更新即add(x)(陣列c[x]在add(x)前為0,不影響sum(x)的統計)。

1 #include2 #include3 #include4

using

namespace

std;

5 typedef long

long

ll;6

#define lowbit(x) ((x)&-(x))

7const

int maxn= 1000006;8

9struct

treearray

12void clear()

13 ll sum(int x) const

19return

ans;20}

21void add(int x, int

d)26}27

} num(maxn);

2829

struct

edge

35} edge[maxn];

3637

void

solve()

52 printf("

test case %d: %lld\n

",++p,inv);53}

5455

intmain()

奇怪的是,這題我wa了近10遍才過(t.t),一開始怎麼也找不著錯的原因,各種long long、__int64、%lld、%i64d的輸入輸出姿勢都試過了,還是wa,沒辦法,只好和標程一點一點地對拍,到最後才發現真正的錯誤是在num.n= m這裡,就是沒處理好樹狀陣列c的n,這種細節問題還是第一次見,不過也需記住這個錯誤,因為樹狀陣列的結構體模板以後應該會經常用到,還是得多刷題了,昨晚竟被老師和師兄說自己的進度太慢,聽後除了慚愧外還有一絲恨鐵不成鋼(我對自己)的心理,大白書,come on!

樹狀陣列求逆序數 poj 2299

突然想起樹狀陣列了,因為突然想起之前從來沒a掉的那個樹狀陣列求逆序數的題,大概意思就是求乙個陣列用氣泡排序排序後的交換次數,因為資料量為5萬,所以o n n 的冒泡模擬肯定是超時的,所以題目的解法可以有兩種,一種是歸併排序,歸併排序的乙個應用正是求逆序數,而另乙個就是樹狀陣列了。思想就是通過將數乙個...

樹狀陣列求逆序對 離散化 poj2299

今天做了乙個樹狀陣列求逆序對的題,需要離散化,看了部落格明白了為什麼要進行離散化,原因是樹狀陣列中的c maxn 陣列其實相當於乙個雜湊操作,如果所給陣列中存在值特別大的元素,陣列就需要離散化 include include include include using namespace std s...

poj 2299 求逆序對

題目大意 求逆序對 解題思路 分治法,類似於歸併排序。a 1.n 將原問題劃分為2個子問題a 1.n 2 a n 2 1.n 並且兩個子陣列已經排好序了,1.n 2的逆序對已經求好,n 2 1.n的逆序對也已經求好了 所以求兩個子問題之間的逆序對,在歸併排序的過程中,當a i a j 時,逆序對的數...