【BZOJ3110】【整体二分+树状数组区间修改/线段树】K大数查询
Description
有N个位置,M个操作。操作有两种,每次操作如果是1 a b c的形式表示在第a个位置到第b个位置,每个位置加入一个数c
如果是2 a b c形式,表示询问从第a个位置到第b个位置,第C大的数是多少。
Input
第一行N,M
接下来M行,每行形如1 a b c或2 a b c
Output
输出每个询问的结果
Sample Input
2 5
1 1 2 1
1 1 2 2
2 1 1 2
2 1 1 1
2 1 2 3
1 1 2 1
1 1 2 2
2 1 1 2
2 1 1 1
2 1 2 3
Sample Output
1
2
1
2
1
HINT
【样例说明】
第一个操作 后位置 1 的数只有 1 , 位置 2 的数也只有 1 。 第二个操作 后位置 1
的数有 1 、 2 ,位置 2 的数也有 1 、 2 。 第三次询问 位置 1 到位置 1 第 2 大的数 是
1 。 第四次询问 位置 1 到位置 1 第 1 大的数是 2 。 第五次询问 位置 1 到位置 2 第 3
大的数是 1 。
N,M<=50000,N,M<=50000
a<=b<=N
1操作中abs(c)<=N
2操作中abs(c)<=Maxlongint
Source
【分析】
我可以吐槽一下数据很水吗?1A了
表示整体二分大法好,离线第K大都不怕。
区间修改用线段树和树状数组都可以水。
1 /* 2 明代杨慎 3 《临江仙·滚滚长江东逝水》 4 5 滚滚长江东逝水,浪花淘尽英雄。 6 是非成败转头空。 7 青山依旧在,几度夕阳红。 8 白发渔樵江渚上,惯看秋月春风。 9 一壶浊酒喜相逢。 10 古今多少事,都付笑谈中。 11 */ 12 #include <iostream> 13 #include <cstdio> 14 #include <algorithm> 15 #include <cstring> 16 #include <vector> 17 #include <utility> 18 #include <iomanip> 19 #include <string> 20 #include <cmath> 21 #include <queue> 22 #include <assert.h> 23 #include <map> 24 #include <ctime> 25 #include <cstdlib> 26 #include <stack> 27 #define LOCAL 28 const int INF = 0x7fffffff; 29 const int MAXN = 300000 + 10; 30 using namespace std; 31 //整体二分+树状数组的区间修改! 32 struct QUESTION{ 33 int l, r; 34 int k, s, cur, type; 35 }q[MAXN]; 36 int id[MAXN]; 37 int Max, Min, n, m, Ans[MAXN]; 38 int c[2][MAXN];//0是普通和,1是全部和 39 int tmp[MAXN], q1[MAXN], q2[MAXN], cnt; 40 41 int lowbit(int x) {return x & -x;} 42 int sum(int k, int x){ 43 int cnt = 0; 44 while (x > 0){ 45 cnt += c[k][x]; 46 x -= lowbit(x); 47 } 48 return cnt; 49 } 50 void add(int k, int x, int val){ 51 while (x <= n){ 52 c[k][x] += val; 53 x += lowbit(x); 54 } 55 return; 56 } 57 //得到一个点真实的前缀和 58 int get(int x){ 59 if (x == 2) 60 printf(""); 61 return sum(1, x) - sum(0, x) * (n - x); 62 } 63 //整体二分 64 void solve(int l, int r, int L, int R){ 65 if (l > r || L == R) return; 66 67 int mid = (L + R) >> 1; 68 for (int i = l; i <= r; i++){ 69 //区间加 70 if (q[id[i]].type == 1 && q[id[i]].k >= mid){//注意是区间第k大 71 int a = q[id[i]].l, b = q[id[i]].r; 72 add(0, a, 1); 73 add(0, b + 1, -1); 74 add(1, a, n - a + 1); 75 add(1, b + 1, - (n - (b + 1) + 1)); 76 }else if (q[id[i]].type == 2) tmp[id[i]] = get(q[id[i]].r) - get(q[id[i]].l - 1); 77 } 78 //清楚标记 79 for (int i = l; i <= r; i++){ 80 if (q[id[i]].type == 1 && q[id[i]].k >= mid){ 81 int a = q[id[i]].l, b = q[id[i]].r; 82 add(0, a, -1); 83 add(0, b + 1, 1); 84 add(1, a, -(n - a + 1)); 85 add(1, b + 1, (n - (b + 1) + 1)); 86 } 87 } 88 int l1 = 0, l2 = 0; 89 //q1放右边,q2放左边 90 for (int i = l; i <= r; i++){ 91 if (q[id[i]].type == 2){ 92 //这个要放在右边 93 if (q[id[i]].cur + tmp[id[i]] > q[id[i]].k - 1){ 94 q1[++l1] = id[i]; 95 Ans[q[id[i]].s] = mid; 96 }else{ 97 q[id[i]].cur += tmp[id[i]]; 98 q2[++l2] = id[i]; 99 } 100 }else{ 101 if (q[id[i]].k >= mid) q1[++l1] = id[i]; 102 else q2[++l2] = id[i]; 103 } 104 } 105 106 for (int i = 1; i <= l2; i++) id[i + l - 1] = q2[i]; 107 for (int i = 1; i <= l1; i++) id[i + l2 + l - 1] = q1[i]; 108 solve(l, l + l2 - 1, L, mid); 109 solve(l + l2, r, mid + 1, R); 110 } 111 void init(){ 112 memset(c, 0, sizeof(c)); 113 scanf("%d%d", &n, &m); 114 cnt = 0;//cnt用来记录询问问题个数 115 Min = INF, Max = -INF; 116 for (int i = 1; i <= m; i++){ 117 int t; 118 scanf("%d", &t); 119 if (t == 1){//插入操作 120 int l, r, x; 121 scanf("%d%d%d", &l, &r, &x); 122 q[i].type = 1; 123 q[i].l = l; q[i].r = r; 124 q[i].k = x; q[i].s = 0; 125 q[i].cur = 0; 126 Max = max(Max, x); 127 Min = min(Min, x); 128 }else if (t == 2){//询问操作 129 int l, r, x; 130 scanf("%d%d%d", &l, &r, &x); 131 q[i].type = 2; 132 q[i].l = l; q[i].r = r; 133 q[i].k = x; q[i].cur = 0; 134 q[i].s = ++cnt;//注意这里x是第k大 135 } 136 } 137 for (int i = 1; i <= m; i++) id[i] = i; 138 //printf("%d %d\n", Max, Min); 139 } 140 141 int main(){ 142 int T; 143 144 init(); 145 solve(1, m, Min, Max + 1); 146 for (int i = 1; i <= cnt; i++) printf("%d\n", Ans[i]); 147 return 0; 148 }