http://acm.hdu.edu.cn/showproblem.php?pid=2665
以前只知道用歸并樹做,查詢時(shí)間復(fù)雜度要 m * ( log n)^3, 昨天學(xué)了一個(gè)劃分樹,查詢只要m * logn 。其實(shí)這題的正解就是使用劃分樹來做。劃分樹跟歸并樹剛好是相反的操作,劃分樹查詢用于查詢[ l , r ]內(nèi)第k大的數(shù), 歸并樹用于查詢某個(gè)數(shù)在[ l , r ]內(nèi)排第幾。
劃分樹的資料比較少,可以參看 小hh 大牛的blog http://www.notonlysuccess.com/?p=142, 或者下面這個(gè)blog http://blog.163.com/tonyshaw@yeah/blog/static/142021718201035102322338/

hdu 2665
1
#include <cstdio>
2
#include <iostream>
3
#include <cmath>
4
#include <complex>
5
#include <algorithm>
6
#include <cstring>
7
#include <queue>
8
using namespace std;
9
10
const int maxn = 100020;
11
int Left[20][maxn], sorted[maxn], tree[20][maxn];
12
13
void build_tree( int L, int R, int v )
14

{
15
int mid = ( L + R ) >> 1;
16
if( L == R ) return;
17
int m = sorted[mid];
18
int same = mid - L + 1; // same表示和m = sorted[mid] 相等且分到左邊的
19
for( int i = L; i <= R; i++ )
20
if( tree[v][i] < m ) same--;
21
int lpos = L;
22
int rpos = mid+1;
23
int ss = 0;
24
for( int i = L; i <= R; i++ )
25
{
26
if( i == L ) Left[v][i] = 0;
27
else Left[v][i] = Left[v][i-1];
28
if( tree[v][i] < m )
29
{
30
tree[v+1][lpos++] = tree[v][i];
31
Left[v][i]++;
32
}
33
else if( tree[v][i] > m )
34
{
35
tree[v+1][rpos++] = tree[v][i];
36
}
37
else
38
{
39
if( ss < same )
40
{
41
tree[v+1][lpos++] = tree[v][i];
42
Left[v][i]++;
43
ss++;
44
}
45
else tree[v+1][rpos++] = tree[v][i];
46
}
47
}
48
build_tree( L, mid, v + 1 );
49
build_tree( mid + 1, R, v + 1 );
50
}
51
52
int query( int L, int R, int l, int r, int k, int v )
53

{
54
int mid = ( L + R ) >> 1;
55
if( l == r ) return tree[v][l];
56
int off; // off表示 [ L, l-1 ]有多少個(gè)分到左邊
57
int cnt; // cnt表示 [ l, r ]有多少個(gè)分到左邊
58
if( l == L )
59
{
60
off = 0;
61
cnt = Left[v][r];
62
}
63
else
64
{
65
off = Left[v][l-1];
66
cnt = Left[v][r] - Left[v][l-1];
67
}
68
if( cnt >= k ) //有多于k個(gè)分到左邊,顯然去左兒子區(qū)間找第k個(gè)
69
{
70
int lnew = L + off;
71
int rnew = lnew + cnt - 1;
72
return query( L, mid, lnew, rnew, k, v + 1 );
73
}
74
else
75
{
76
off = l - L - off; // off表示 [ L, l-1 ]有多少個(gè)分到右邊
77
k = k - cnt;
78
cnt = r - l + 1 - cnt; // cnt表示 [ l, r ]有多少個(gè)分到右邊
79
int lnew = mid + 1 + off;
80
int rnew = lnew + cnt - 1;
81
return query( mid + 1, R, lnew, rnew, k, v + 1 );
82
}
83
}
84
85
int main(int argc, char *argv[])
86

{
87
int t, n, m, l, r, k;
88
scanf("%d",&t);
89
while( t-- )
90
{
91
scanf("%d%d",&n,&m);
92
for( int i = 1; i <= n; i++ )
93
{
94
scanf("%d",&tree[0][i]);
95
sorted[i] = tree[0][i];
96
}
97
sort( sorted + 1, sorted + n + 1 );
98
build_tree( 1, n, 0 );
99
for( int i = 0; i < m; i++ )
100
{
101
scanf("%d%d%d",&l,&r,&k);
102
printf("%d\n",query( 1, n, l, r, k, 0 ) );
103
}
104
}
105
return 0;
106
}
107
以前只知道用歸并樹做,查詢時(shí)間復(fù)雜度要 m * ( log n)^3, 昨天學(xué)了一個(gè)劃分樹,查詢只要m * logn 。其實(shí)這題的正解就是使用劃分樹來做。劃分樹跟歸并樹剛好是相反的操作,劃分樹查詢用于查詢[ l , r ]內(nèi)第k大的數(shù), 歸并樹用于查詢某個(gè)數(shù)在[ l , r ]內(nèi)排第幾。
劃分樹的資料比較少,可以參看 小hh 大牛的blog http://www.notonlysuccess.com/?p=142, 或者下面這個(gè)blog http://blog.163.com/tonyshaw@yeah/blog/static/142021718201035102322338/


1

2

3

4

5

6

7

8

9

10

11

12

13

14



15

16

17

18

19

20

21

22

23

24

25



26

27

28

29



30

31

32

33

34



35

36

37

38



39

40



41

42

43

44

45

46

47

48

49

50

51

52

53



54

55

56

57

58

59



60

61

62

63

64



65

66

67

68

69



70

71

72

73

74

75



76

77

78

79

80

81

82

83

84

85

86



87

88

89

90



91

92

93



94

95

96

97

98

99

100



101

102

103

104

105

106

107
