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 '!;;;;;;;;!$@###&|;;|%!;!$|;;;;|&&;. 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 #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 }