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 }
View Code

 

posted @ 2018-12-17 00:08  Jiaaaaaaaqi  阅读(498)  评论(0编辑  收藏  举报