前幾天開始看樹狀陣列了,然後開始找題來刷。
首先是 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 #include4using
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 #include4using
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 時,逆序對的數...