K大数查询 bzoj 3110
Posted 草根柴鸡
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了K大数查询 bzoj 3110相关的知识,希望对你有一定的参考价值。
K大数查询
【问题描述】
有N个位置,M个操作。操作有两种,每次操作如果是1 a b c的形式表示在第a个位置到第b个位置,每个位置加入一个数c如果是2 a b c形式,表示询问从第a个位置到第b个位置,第C大的数是多少。
【输入格式】
第一行N,M
接下来M行,每行形如1 a b c或2 a b c
【输出格式】
输出每个询问的结果
【样例输入】
2 5
1 1 2 1
1 1 2 2
2 1 1 2
2 1 1 1
2 1 2 3
【样例输出】
1
2
1
【样例说明】
第一个操作 后位置 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操作中c<=Maxlongint
题解:
主要算法:整体二分;线段树;
我们将询问离线,做整体二分
题目中有负数,那么我们转化一下,将每个数变为n-i+1,输出答案时再变为n-ans+1
对于一个操作1,如果这个操作加入的c是不超过mid的
用线段树在区间内加1,表示此区间小于等于mid的数多了一个,那么将它放置到左区间
否则将其放置到右区间,表示这个操作的贡献在右区间
对于一个操作2,查询在区间内小于等于mid的数的个数tot
如果tot超过k,将其放置到左区间,表示答案在左区间
否则将k减去tot,放置到右区间,表示需要在右区间找k-tot大的数
1 #include<algorithm> 2 #include<iostream> 3 #include<cstring> 4 #include<cstdlib> 5 #include<cstdio> 6 #include<cmath> 7 using namespace std; 8 struct S { long long x, y, z, id, flag; } a[1000001], c[1000001]; 9 bool lr[1000001]; 10 long long n, m, tot, maxx = -2147483647; 11 long long sum[1000001], ans[1000001], node[1000001]; 12 void Down(long long k, int l, int r) 13 { 14 if(node[k] != 0) 15 { 16 int mi = (l + r) >> 1; 17 node[k * 2] += node[k]; 18 node[k * 2 + 1] += node[k]; 19 sum[k * 2] += node[k] * (mi - l + 1); 20 sum[k * 2 + 1] += node[k] * (r - mi); 21 node[k] = 0; 22 } 23 } 24 void Inc(int k, int l, int r, int x, int y, int z) 25 { 26 if(x <= l && r <= y) 27 { 28 sum[k] += z * (r - l + 1); 29 node[k] += z; 30 return; 31 } 32 Down(k, l, r); 33 int mi = (l + r) >> 1; 34 if(mi >= x) Inc(k * 2, l, mi, x, y, z); 35 if(mi < y) Inc(k * 2 + 1, mi + 1, r, x, y, z); 36 sum[k] = sum[k * 2] + sum[k * 2 + 1]; 37 } 38 long long Sum(long long k, long long l, long long r, long long x, long long y) 39 { 40 if(x <= l && r <= y) return sum[k]; 41 Down(k, l, r); 42 long long mi = (l + r) >> 1, res = 0; 43 if(mi >= x) res += Sum(k * 2, l, mi, x, y); 44 if(mi < y) res += Sum(k * 2 + 1, mi + 1, r, x, y); 45 return res; 46 } 47 void Two(long long x, long long y, long long l, long long r) 48 { 49 cout<<x<<‘ ‘<<y<<‘ ‘<<l<<‘ ‘<<r<<endl; 50 long long mi = (l + r) >> 1; 51 if(l == r) 52 { 53 for(int i = x; i <= y; ++i) 54 if(a[i].flag) 55 ans[a[i].id] = mi; 56 return; 57 } 58 long long temp, s = x; 59 for(int i = x; i <= y; ++i) 60 { 61 if(a[i].flag) 62 { 63 temp = Sum(1, 1, 2 * n + 1, a[i].x, a[i].y); 64 if(temp < a[i].z) 65 { 66 a[i].z -= temp; 67 lr[i] = false; 68 } 69 else 70 { 71 ++s; 72 lr[i] = true; 73 } 74 } 75 else 76 { 77 if(a[i].z <= mi) 78 { 79 Inc(1, 1, 2 * n + 1, a[i].x, a[i].y, 1); 80 ++s; 81 lr[i] = true; 82 } 83 else lr[i] = false; 84 } 85 } 86 for(int i = x; i <= y; ++i) 87 if(!a[i].flag && a[i].z <= mi) 88 Inc(1, 1, 2 * n + 1, a[i].x, a[i].y, -1); 89 long long o = x; 90 for(int i = x; i <= y; ++i) 91 if(lr[i]) c[o++] = a[i]; 92 else c[s++] = a[i]; 93 for(int i = x; i <= y; ++i) a[i] = c[i]; 94 Two(x, o - 1, l, mi), Two(o, y, mi + 1, r); 95 } 96 int main() 97 { 98 scanf("%lld%lld", &n, &m); 99 for(int i = 1; i <= m; ++i) 100 { 101 scanf("%lld%lld%lld%lld", &a[i].flag, &a[i].x, &a[i].y, &a[i].z); 102 --a[i].flag; 103 if(!a[i].flag) 104 { 105 a[i].z = n - a[i].z + 1; 106 maxx = (a[i].z > maxx) ? a[i].z : maxx; 107 } 108 else a[i].id = ++tot; 109 } 110 Two(1, m, 1, maxx); 111 for(int i = 1; i <= tot; ++i) printf("%lld\n", n - ans[i] + 1); 112 }
以上是关于K大数查询 bzoj 3110的主要内容,如果未能解决你的问题,请参考以下文章
bzoj 3110 [Zjoi2013]K大数查询 整体二分
BZOJ 3110 [Zjoi2013]K大数查询 ——整体二分