1. 程式人生 > >Dynamic Rankings ZOJ - 2112(主席樹+樹狀陣列)

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