[BZOJ1901]Zju2112 Dynamic Rankings
[BZOJ1901]Zju2112 Dynamic Rankings
试题描述
给定一个含有n个数的序列a[1],a[2],a[3]……a[n],程序必须回答这样的询问:对于给定的i,j,k,在a[i],a[i+1],a[i+2]……a[j]中第k小的数是多少(1≤k≤j-i+1),并且,你可以改变一些a[i]的值,改变后,程序还能针对改变后的a继续回答上面的问题。你需要编一个这样的程序,从输入文件中读入序列a,然后读入一系列的指令,包括询问指令和修改指令。对于每一个询问指令,你必须输出正确的回答。
输入
第一行有两个正整数n(1≤n≤10000),m(1≤m≤10000)。分别表示序列的长度和指令的个数。第二行有n个数,表示a[1],a[2]……a[n],这些数都小于10^9。接下来的m行描述每条指令,每行的格式是下面两种格式中的一种。 Q i j k 或者 C i t Q i j k (i,j,k是数字,1≤i≤j≤n, 1≤k≤j-i+1)表示询问指令,询问a[i],a[i+1]……a[j]中第k小的数。C i t (1≤i≤n,0≤t≤10^9)表示把a[i]改变成为t。
输出
对于每一次询问,你都需要输出他的答案,每一个输出占单独的一行。
输入示例
5 3 3 2 1 4 7 Q 1 4 3 C 2 6 Q 2 5 3
输出示例
3 6
数据规模及约定
20%的数据中,m,n≤100; 40%的数据中,m,n≤1000; 100%的数据中,m,n≤10000。
题解
线段树套线段树来一发。
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 | #include <iostream> #include <cstdio> #include <algorithm> #include <cmath> #include <stack> #include <vector> #include <queue> #include <cstring> #include <string> #include <map> #include <set> using namespace std; int read() { int x = 0, f = 1; char c = getchar(); while (!isdigit(c)){ if (c == '-' ) f = -1; c = getchar(); } while (isdigit(c)){ x = x * 10 + c - '0' ; c = getchar(); } return x * f; } #define maxval 1000000000 #define maxn 10010 #define maxnode 4200010 int n; int ToT, sumv[maxnode], lc[maxnode], rc[maxnode], recs[maxnode], rcnts; int getsnode() { if (rcnts) { int o = recs[rcnts--]; sumv[o] = lc[o] = rc[o] = 0; return o; } return ++ToT; } void update( int & o, int l, int r, int x, int v) { if (!o) o = getsnode(); if (l == r) { sumv[o] += v; if (!sumv[o]) recs[++rcnts] = o, o = 0; return ; } int mid = l + r >> 1; if (x <= mid) update(lc[o], l, mid, x, v); else update(rc[o], mid + 1, r, x, v); sumv[o] = (lc[o] ? sumv[lc[o]] : 0) + (rc[o] ? sumv[rc[o]] : 0); if (!sumv[o]) recs[++rcnts] = o, o = 0; return ; } int rt[maxn<<2], No[maxn], cn; void modify( int L, int R, int o, int p, int x, int v) { update(rt[o], 0, maxval, x, v); if (L == R) return ; int M = L + R >> 1, lc = o << 1, rc = lc | 1; if (p <= M) modify(L, M, lc, p, x, v); else modify(M+1, R, rc, p, x, v); return ; } void query( int L, int R, int o, int ql, int qr) { if (ql <= L && R <= qr){ No[++cn] = rt[o]; return ; } int M = L + R >> 1, lc = o << 1, rc = lc | 1; if (ql <= M) query(L, M, lc, ql, qr); if (qr > M) query(M+1, R, rc, ql, qr); return ; } int solve( int ql, int qr, int k) { cn = 0; query(1, n, 1, ql, qr); int l = 0, r = maxval; while (l < r) { int mid = l + r >> 1, sum = 0; for ( int i = 1; i <= cn; i++) if (No[i] && lc[No[i]]) sum += sumv[lc[No[i]]]; if (sum < k) { k -= sum; l = mid + 1; for ( int i = 1; i <= cn; i++) if (No[i]) No[i] = rc[No[i]]; } else { r = mid; for ( int i = 1; i <= cn; i++) if (No[i]) No[i] = lc[No[i]]; } } return l; } int val[maxn]; int main() { n = read(); int q = read(); for ( int i = 1; i <= n; i++) val[i] = read(), modify(1, n, 1, i, val[i], 1); char cmd[2]; while (q--) { scanf( "%s" , cmd); if (cmd[0] == 'Q' ) { int ql = read(), qr = read(), k = read(); printf( "%d\n" , solve(ql, qr, k)); } if (cmd[0] == 'C' ) { int x = read(), v = read(); modify(1, n, 1, x, val[x], -1); val[x] = v; modify(1, n, 1, x, val[x], 1); } } return 0; } |
树状数组套主席树来一发。
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 | #include <iostream> #include <cstdio> #include <algorithm> #include <cmath> #include <stack> #include <vector> #include <queue> #include <cstring> #include <string> #include <map> #include <set> using namespace std; int read() { int x = 0, f = 1; char c = getchar(); while (!isdigit(c)){ if (c == '-' ) f = -1; c = getchar(); } while (isdigit(c)){ x = x * 10 + c - '0' ; c = getchar(); } return x * f; } #define maxval 1000000000 #define maxn 10010 #define maxnode 4200010 int n, rt[maxn], val[maxn]; int ToT, sumv[maxnode], lc[maxnode], rc[maxnode]; void update( int & y, int x, int l, int r, int p, int v) { sumv[y = ++ToT] = sumv[x] + v; if (l == r) return ; int mid = l + r >> 1; lc[y] = lc[x]; rc[y] = rc[x]; if (p <= mid) update(lc[y], lc[x], l, mid, p, v); else update(rc[y], rc[x], mid + 1, r, p, v); return ; } int Rt[maxn], lrt[maxn], rrt[maxn], cl, cr; void modify( int p, int x, int v) { for (; p <= n; p += p & -p) update(Rt[p], Rt[p], 0, maxval, x, v); return ; } void query( int ql, int qr) { cl = cr = 0; ql--; lrt[++cl] = rt[ql]; for (; ql; ql -= ql & -ql) lrt[++cl] = Rt[ql]; rrt[++cr] = rt[qr]; for (; qr; qr -= qr & -qr) rrt[++cr] = Rt[qr]; return ; } int solve( int ql, int qr, int k) { query(ql, qr); int l = 0, r = maxval; while (l < r) { int mid = l + r >> 1, sum = 0; for ( int i = 1; i <= cr; i++) if (rrt[i] && lc[rrt[i]]) sum += sumv[lc[rrt[i]]]; for ( int i = 1; i <= cl; i++) if (lrt[i] && lc[lrt[i]]) sum -= sumv[lc[lrt[i]]]; if (sum < k) { k -= sum; l = mid + 1; for ( int i = 1; i <= cl; i++) if (lrt[i]) lrt[i] = rc[lrt[i]]; for ( int i = 1; i <= cr; i++) if (rrt[i]) rrt[i] = rc[rrt[i]]; } else { r = mid; for ( int i = 1; i <= cl; i++) if (lrt[i]) lrt[i] = lc[lrt[i]]; for ( int i = 1; i <= cr; i++) if (rrt[i]) rrt[i] = lc[rrt[i]]; } } return l; } int main() { n = read(); int q = read(); for ( int i = 1; i <= n; i++) { val[i] = read(); update(rt[i], rt[i-1], 0, maxval, val[i], 1); } char cmd[2]; while (q--) { scanf( "%s" , cmd); if (cmd[0] == 'Q' ) { int ql = read(), qr = read(), k = read(); printf( "%d\n" , solve(ql, qr, k)); } if (cmd[0] == 'C' ) { int x = read(), v = read(); modify(x, val[x], -1); modify(x, val[x] = v, 1); } } return 0; } |
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步