Dynamic Rankings ZOJ - 2112(主席樹+樹狀陣列)
The Company Dynamic Rankings has developed a new kind of computer that is no longer satisfied with the query like to simply find the k-th smallest number of the given N numbers. They have developed a more powerful system such that for N numbers a[1], a[2], ..., a[N], you can ask it like: what is the k-th smallest number of a[i], a[i+1], ..., a[j]? (For some i<=j, 0<k<=j+1-i that you have given to it). More powerful, you can even change the value of some a[i], and continue to query, all the same.
Your task is to write a program for this computer, which
- Reads N numbers from the input (1 <= N <= 50,000)
- Processes M instructions of the input (1 <= M <= 10,000). These instructions include querying the k-th smallest number of a[i], a[i+1], ..., a[j] and change some a[i] to t.
Input
The first line of the input is a single number X (0 < X <= 4), the number of the test cases of the input. Then X blocks each represent a single test case.
The first line of each block contains two integers N and M, representing N numbers and M instruction. It is followed by N lines. The (i+1)-th line represents the number a[i]. Then M lines that is in the following format
Q i j k or
C i t
It represents to query the k-th number of a[i], a[i+1], ..., a[j] and change some a[i] to t, respectively. It is guaranteed that at any time of the operation. Any number a[i] is a non-negative integer that is less than 1,000,000,000.
There're NO breakline between two continuous test cases.
Output
For each querying operation, output one integer to represent the result. (i.e. the k-th smallest number of a[i], a[i+1],..., a[j])
There're NO breakline between two continuous test cases.
Sample Input
2
5 3
3 2 1 4 7
Q 1 4 3
C 2 6
Q 2 5 3
5 3
3 2 1 4 7
Q 1 4 3
C 2 6
Q 2 5 3
Sample Output
3
6
3
6
參考了大佬的部落格:https://www.cnblogs.com/Empress/p/4659824.html
題意就是求區間內的第 k 小的數,但是會改變區間裡的數。
因為數很大,所以首先要把所有的 a[i] ,包括更新過程的 a[i],然後拿去離散化。
對於求區間第 k 大,基本思想還是和靜態區間求第 k 小一樣,先求字首和,然後相減。然後得到區間內的數量關係,就可以求區間內的第 k 小了。但是這裡有更新操作,原本的求區間的 node[node[y].l].sum - node[node[x].l].sum 就不能直接用了。
這裡我們維護兩顆完全不同的樹,對一開始的數列用普通的主席樹來維護,對於更新操作,用一個樹狀陣列來維護,樹狀陣列的每個節點都是一顆樹,因為是單點更新,每棵樹都只要更新 log(n) 個節點,用主席樹來更新。(一個只維護初始的樣子,一個只維護更新的樣子)
然後這樣的話,我們只需要想辦法求出在 R 樹和 L 樹在某個區間上的左邊部分的差值,在加上 node[node[y].l].sum - node[node[x].l].sum,就可以確定第 k 小在這個區間的左邊還是右邊。然後就可以開始用主席樹的套路了。
然後這裡為了求 R 樹和 L 樹在某個區間內的差值,我們定義一個 use 表示樹狀陣列的目前結點對應的樹是哪一顆,use 的更新類似於主席樹遞迴時候從x -> node[x].l 這樣子的過程,都是為了把它往左整體往左更新。.......其實還是感覺很抽象的東西
1 /* 2 . 3 ';;;;;. 4 '!;;;;;;!;` 5 '!;|&#@|;;;;!: 6 `;;!&####@|;;;;!: 7 .;;;!&@$$%|!;;;;;;!'.`:::::'. 8 '!;;;;;;;;[email protected]###&|;;|%!;!$|;;;;|&&;. 9 :!;;;;[email protected]&%|;;;;;;;;;|!::!!:::;!$%;!$%` '!%&#########@$!:. 10 ;!;;!!;;;;;|$$&@##$;;;::'''''::;;;;|&|%@$|;;;;;;;;;;;;;;;;!$; 11 ;|;;;;;;;;;;;;;;;;;;!%@#####&!:::;!;;;;;;;;;;!&####@%!;;;;$%` 12 `!!;;;;;;;;;;!|%%|!!;::;;|@##%|$|;;;;;;;;;;;;!|%$#####%;;;%&; 13 :@###&!:;;!!||%%%%%|!;;;;;||;;;;||!$&&@@%;;;;;;;|$$##$;;;%@| 14 ;|::;;;;;;;;;;;;|&&$|;;[email protected]&$!;;;;!;;;;;;;;;;;;;;;;!%|;;;%@%. 15 `!!;;;;;;;!!!!;;;;;[email protected]@@&&&&&@$!;!%|;;;;!||!;;;;;!|%%%!;;%@|. 16 %&&$!;;;;;!;;;;;;;;;;;|$&&&&&&&&&@@%!%%;!||!;;;;;;;;;;;;;$##! 17 !%;;;;;;!%!:;;;;;;;;;;!$&&&&&&&&&&@##&%|||;;;!!||!;;;;;;;$&: 18 ':|@###%;:;;;;;;;;;;;;!%$&&&&&&@@$!;;;;;;;!!!;;;;;%&!;;|&%. 19 [email protected]|;;;;;;;;;;;;;;;;;;|%|$&&$%&&|;;;;;;;;;;;;!;;;;;!&@@&' 20 .:%#&!;;;;;;;;;;;;;;!%|$$%%&@%;;;;;;;;;;;;;;;;;;;!&@: 21 .%$;;;;;;;;;;;;;;;;;;|[email protected]&|;;;;;;;;;;;;;;;;;;;;%@%. 22 !&!;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;|@#; 23 `%$!;;;;;;;;;;;[email protected]|;;;;;;;;;;;;;;;;;;;;;;;;!%[email protected]#@|. 24 .|@%!;;;;;;;;;!$&%||;;;;;;;;;;;;;;;;;!%[email protected]#|. 25 ;&$!;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;%#####|. 26 |##$|!;;;;;;::'':;;;;;;;;;;;;;!%[email protected]#@; 27 ;@&|;;;;;;;::'''''':;;;;;;;|$&@###@|` 28 .%##@|;;;;:::''''''''''::;!%&##$' 29 `$##@[email protected]@&|!!;;;:'''''::::;;;;;|&#%. 30 ;&@##&$%!;;;;;;::''''''''::;!|%[email protected]#@&@@: 31 .%@&$$|;;;;;;;;;;:'''':''''::;;;%@#@@#%. 32 :@##@###@$$$$$|;;:'''':;;!!;;;;;;!$#@@#$;` 33 `%@$$|;;;;;;;;:'''''''::;;;;|%$$|!!&###&' 34 |##&%!;;;;;::''''''''''''::;;;;;;;[email protected]&:`!' 35 :;[email protected]$|;;;;;;;::''''''''''':;;;;;;;;!%&@$: [email protected]#$' 36 |##@@&%;;;;;::''''''''':;;;;;;;!%&@#@$%: '%%!%&; 37 |&%!;;;;;;;%$!:''''''':|%!;;;;;;;;|&@%||` '%$|!%&; 38 |@%!;;!!;;;||;:'''''':;%$!;;;;!%%%&#&%$&: .|%;:!&%` 39 [email protected]&%;;;;;;;||;;;:''::;;%$!;;;;;;;|&@%;!$; `%&%!!$&: 40 '$$|;!!!!;;||;;;;;;;;;;%%;;;;;;;|@@|!$##; !$!;:!$&: 41 |#&|;;;;;;!||;;;;;;;;!%|;;;;!$##$;;;;|%' `%$|%%;|&$' 42 |&%!;;;;;;|%;;;;;;;;$$;;;;;;|&&|!|%&&; .:%&$!;;;:[email protected]! 43 `%#&%!!;;;;||;;;;;!$&|;;;!%%%@&!;;;!!;;;|%!;;%@$!%@! 44 !&!;;;;;;;;;||;;%&!;;;;;;;;;%@&!;;!&$;;;|&%;;;%@%` 45 '%|;;;;;;;;!!|$|%&%;;;;;;;;;;|&#&|!!||!!|%[email protected]@|' 46 .!%%&%'`|$; :|$#%|@#&;%#%. 47 */ 48 #include <map> 49 #include <set> 50 #include <list> 51 #include <ctime> 52 #include <cmath> 53 #include <stack> 54 #include <queue> 55 #include <string> 56 #include <vector> 57 #include <cstdio> 58 #include <bitset> 59 #include <cstdlib> 60 #include <cstring> 61 #include <iostream> 62 #include <algorithm> 63 #define lowbit(x) x & (-x) 64 #define mes(a, b) memset(a, b, sizeof a) 65 #define fi first 66 #define se second 67 #define pii pair<int, int> 68 #define INOPEN freopen("in.txt", "r", stdin) 69 #define OUTOPEN freopen("out.txt", "w", stdout) 70 71 typedef unsigned long long int ull; 72 typedef long long int ll; 73 const int maxn = 5e4 + 10; 74 const int maxm = 1e5 + 10; 75 const ll mod = 1e9 + 7; 76 const ll INF = 1e18 + 100; 77 const int inf = 0x3f3f3f3f; 78 const double pi = acos(-1.0); 79 const double eps = 1e-8; 80 using namespace std; 81 82 int n, m; 83 int cas, tol, T; 84 struct Node{ 85 int l, r; 86 int sum; 87 } node[maxn * 40]; 88 struct Ask{ 89 int l, r, k; 90 int id; 91 } ask[maxn / 3]; 92 int a[maxn]; 93 int S[maxn]; //樹狀陣列的根 94 int rt[maxn]; //主席樹的根 95 int use[maxn]; //use表示樹狀陣列的各個結點對應的樹是哪一顆 96 vector<int> vv; 97 int L, R; 98 99 int getid(int x) { 100 return lower_bound(vv.begin(), vv.end(), x) - vv.begin() + 1; 101 } 102 103 void init() { 104 tol = 0; 105 mes(a, 0); 106 mes(S, 0); 107 mes(rt, 0); 108 vv.clear(); 109 mes(use, 0); 110 mes(ask, 0); 111 mes(node, 0); 112 } 113 114 int Sum(int pos) { 115 // 各顆樹從 S[i] 走到了 used[i] 的位置,然後求左邊部分的和 116 int ans = 0; 117 for(int i=pos; i; i-=lowbit(i)) 118 ans += node[node[use[i]].l].sum; 119 return ans; 120 } 121 122 void update(int l, int r, int &x, int y, int pos, int val) { 123 x = ++tol; 124 node[tol] = node[y]; 125 node[tol].sum += val; 126 if(l == r) return ; 127 int mid = (l + r) >> 1; 128 if(pos <= mid) 129 update(l, mid, node[x].l, node[y].l, pos, val); 130 else 131 update(mid+1, r, node[x].r, node[y].r, pos, val); 132 } 133 134 void add(int pos, int k, int v) { 135 for(int i=pos; i<=n; i+=lowbit(i)) { 136 update(1, vv.size(), S[i], S[i], k, v); 137 } 138 } 139 140 int query(int l, int r, int x, int y, int k) { 141 if(l == r) return l; 142 int mid = (l + r) >> 1; 143 //注意樹狀陣列是 L 和 R 144 //因為是找 L 和 R 樹鏈上的到達 use 位置的值 145 int tmp = Sum(R) - Sum(L) + node[node[y].l].sum - node[node[x].l].sum; 146 if(k <= tmp) { 147 //往現在到達的x,y位置的兩棵樹的左邊查詢 148 //把 L 和 R 這路徑上的所有樹狀陣列都往左子樹更新 149 for(int i=L; i; i-=lowbit(i)) use[i] = node[use[i]].l; 150 for(int i=R; i; i-=lowbit(i)) use[i] = node[use[i]].l; 151 return query(l, mid, node[x].l, node[y].l, k); 152 } else { 153 //往現在到達的x,y位置的兩棵樹的右邊查詢 154 //把 L 和 R 這路徑上的所有樹狀陣列都往右子樹更新 155 for(int i=L; i; i-=lowbit(i)) use[i] = node[use[i]].r; 156 for(int i=R; i; i-=lowbit(i)) use[i] = node[use[i]].r; 157 return query(mid+1, r, node[x].r, node[y].r, k-tmp); 158 } 159 } 160 161 int main() { 162 scanf("%d", &T); 163 while(T--) { 164 init(); 165 scanf("%d%d", &n, &m); 166 for(int i=1; i<=n; i++) { 167 scanf("%d", &a[i]); 168 vv.push_back(a[i]); 169 } 170 for(int i=1; i<=m; i++) { 171 char ss[5]; 172 scanf("%s", ss); 173 if(ss[0] == 'Q') { 174 ask[i].id = 1; 175 scanf("%d%d%d", &ask[i].l, &ask[i].r, &ask[i].k); 176 } else { 177 ask[i].id = 0; 178 scanf("%d%d", &ask[i].l, &ask[i].r); 179 vv.push_back(ask[i].r); 180 } 181 } 182 sort(vv.begin(), vv.end()); //離散化一下 183 vv.erase(unique(vv.begin(), vv.end()), vv.end()); 184 for(int i=1; i<=n; i++) { 185 int id = getid(a[i]); //第一顆樹 186 update(1, vv.size(), rt[i], rt[i-1], id, 1); 187 //vv裡面的數可能不止有 n 個,切記不可以用 n 188 } 189 for(int i=1; i<=m; i++) { 190 if(ask[i].id) { 191 L = ask[i].l-1, R = ask[i].r; 192 //從 L 和 R 位置開始, use 表示的從 L, R 的S根開始 193 for(int j=L; j; j-=lowbit(j)) use[j] = S[j]; 194 for(int j=R; j; j-=lowbit(j)) use[j] = S[j]; 195 int pos = query(1, vv.size(), rt[L], rt[R], ask[i].k); 196 printf("%d\n", vv[pos-1]); 197 } else { //第二顆樹 198 int id = getid(a[ask[i].l]); 199 add(ask[i].l, id, -1); //先把之前的清除掉,在加現在的 200 id = getid(ask[i].r); 201 add(ask[i].l, id, 1); 202 a[ask[i].l] = ask[i].r; 203 } 204 } 205 } 206 return 0; 207 }View Code