ZOJ 2112 Dynamic Rankings(动态求区间第k大+整体二分)
2016-05-01 20:37
423 查看
Dynamic Rankings
Time Limit: 10 Seconds Memory Limit: 32768 KB
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
, 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
solution:
动态求区间第k大,可用树状数组套主席树,但对于这道题来说会TLE,其他树套树都不如整体二分快。
将修改看成是删除和添加,那么总共有添加、删除和查询三种操作。
这个判定操作是属于左区间还是右区间的过程就是通过比较比二分的mid大的数的个数和k。同时我们看到,如果比二分的mid小的数的个数小于k了,我们是要去寻找大的答案,那么这些比mid大的数在以后的递归里始终会对答案有贡献,所以我们没必要去做重复的工作,只需要把这些数的个数累积到贡献里,以后递归的时候就不用考虑这些数了。如果比二分的mid小的数大于k,就要寻找小的答案,那么那些本来就比mid大的添加和删除操作对答案不会产生影响,可以放在答案右区间。
详细见代码~
Time Limit: 10 Seconds Memory Limit: 32768 KB
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
, 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
solution:
动态求区间第k大,可用树状数组套主席树,但对于这道题来说会TLE,其他树套树都不如整体二分快。
将修改看成是删除和添加,那么总共有添加、删除和查询三种操作。
这个判定操作是属于左区间还是右区间的过程就是通过比较比二分的mid大的数的个数和k。同时我们看到,如果比二分的mid小的数的个数小于k了,我们是要去寻找大的答案,那么这些比mid大的数在以后的递归里始终会对答案有贡献,所以我们没必要去做重复的工作,只需要把这些数的个数累积到贡献里,以后递归的时候就不用考虑这些数了。如果比二分的mid小的数大于k,就要寻找小的答案,那么那些本来就比mid大的添加和删除操作对答案不会产生影响,可以放在答案右区间。
详细见代码~
#include<iostream> #include<cstdio> #include<algorithm> #include<cmath> #include<cstring> #define mem(a) memset(a,0,sizeof(a)) const int maxn = 220000; const int inf = 1e9 + 7; using namespace std; struct query { int x, y, k, s, tp, cur;//添加:tp=1 删除:tp=2 查询:tp=3 }q[maxn], q1[maxn], q2[maxn]; int a[maxn], ans[maxn], tmp[maxn], bit[maxn]; int n, m, num, cnt;//cnt为查询的顺序 void init() { cnt = 0;num = 0; mem(bit); mem(tmp); mem(q); } void add(int x, int y) { for (int i = x; i <= n; i += (i&-i)) bit[i] += y; } int sum(int x) { int tmp = 0; for (int i = x; i>0; i -= (i&-i)) tmp += bit[i]; return tmp; } void divide(int head, int tail, int l, int r)//表示下标head到tail的操作的答案区间为[l,r] { if (head>tail) return; if (l == r) { for (int i = head; i <= tail; i++) if (q[i].tp == 3) ans[q[i].s] = l; return; } int mid = (l + r) >> 1; for (int i = head; i <= tail; i++) { if (q[i].tp == 1 && q[i].y <= mid) add(q[i].x, 1); else if (q[i].tp == 2 && q[i].y <= mid) add(q[i].x, -1); else if (q[i].tp == 3) tmp[i] = sum(q[i].y) - sum(q[i].x - 1); } for (int i = head; i <= tail; i++) { if (q[i].tp == 1 && q[i].y <= mid) add(q[i].x, -1); else if (q[i].tp == 2 && q[i].y <= mid) add(q[i].x, 1); } int l1 = 0, l2 = 0; for (int i = head; i <= tail; i++) if (q[i].tp == 3) { if (q[i].cur + tmp[i]>q[i].k - 1)//q[i].cur+tmp[i]表示累积了多少个数 q1[++l1] = q[i]; else { q[i].cur += tmp[i]; q2[++l2] = q[i]; } } else { if (q[i].y <= mid) q1[++l1] = q[i]; else q2[++l2] = q[i]; } for (int i = 1; i <= l1; i++) q[head + i - 1] = q1[i]; for (int i = 1; i <= l2; i++) q[head + l1 + i - 1] = q2[i]; divide(head, head + l1 - 1, l, mid); divide(head + l1, tail, mid + 1, r); } int main() { int T; scanf("%d", &T); while (T--) { scanf("%d%d", &n, &m); init(); for (int i = 1; i <= n; i++) { scanf("%d", &a[i]); q[++num].x = i; q[num].y = a[i]; q[num].tp = 1; q[num].s = 0; } char op[10]; int x, y, z; for (int i = 1; i <= m; i++) { scanf("%s", op); if (op[0] == 'Q') { scanf("%d%d%d", &x, &y, &z); q[++num].x = x, q[num].y = y, q[num].k = z; q[num].tp = 3; q[num].s = ++cnt; } else { scanf("%d%d", &x, &y); q[++num].x = x; q[num].y = a[x]; q[num].tp = 2; q[num].s = 0; q[++num].x = x; q[num].y = y; q[num].tp = 1; q[num].s = 0; a[x] = y; } } divide(1, num, 0, inf); for (int i = 1; i <= cnt; i++) printf("%d\n", ans[i]); } return 0; }
相关文章推荐
- nyoj 44 子串和
- nyoj 44 子串和
- Google机器学习教程心得(二)决策树与可视化
- 20145216史婧瑶《Java程序设计》第9周学习总结
- storm启动遇到问题storm expected <block end>, but found BlockMappingStart in 'reader', line 23, column 2:
- 简单的文本处理
- unity3d圆形空气墙,行动边界的实现
- 面试1——TCP和UDP区别
- Linux内核学习总结
- 20145216史婧瑶《Java程序设计》第9周学习总结
- RxJava之Subject
- 搭建环境
- 学习Linux内核的心得
- hibernate 创建工厂类
- 基于 HandlerThread 的 IntentService
- php实现扫码支付
- February 29(模拟)
- kali无线渗透之aireplay-ng
- struts学习-HelloWorld
- Canvas和Paint的常用方法