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 }
时间: 2024-10-10 22:26:37

K大数查询 bzoj 3110的相关文章

【ZJOI2013】k大数查询 BZOJ 3110

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 Sample Output 1 2 1 HINT

K大数查询 HYSBZ - 3110

K大数查询 HYSBZ - 3110 本来是刷整体二分的,被这个sb题折腾了一下午,用cin就RE, 用scanf就过了=_= 收获就是偶然学到了树状数组区间修改区间查询的写法吧... 1 #include <iostream> 2 #include <cstring> 3 #include <cstdio> 4 using namespace std; 5 #define LL long long 6 const int maxn = 1e5 + 10; 7 cons

树套树专题——bzoj 3110: [Zjoi2013] K大数查询 &amp; 3236 [Ahoi2013] 作业 题解

[原题1] 3110: [Zjoi2013]K大数查询 Time Limit: 20 Sec  Memory Limit: 512 MB Submit: 978  Solved: 476 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 Outpu

BZOJ 3110: [Zjoi2013]K大数查询( 树状数组套主席树 )

BIT+(可持久化)权值线段树, 用到了BIT的差分技巧. 时间复杂度O(Nlog^2(N)) ----------------------------------------------------------------------------------------- #include<cstdio> #include<cctype> #include<cstring> #include<algorithm> using namespace std;

BZOJ 3110: [Zjoi2013]K大数查询 [树套树]

3110: [Zjoi2013]K大数查询 Time Limit: 20 Sec  Memory Limit: 512 MBSubmit: 6050  Solved: 2007[Submit][Status][Discuss] Description 有N个位置,M个操作.操作有两种,每次操作如果是1 a b c的形式表示在第a个位置到第b个位置,每个位置加入一个数c如果是2 a b c形式,表示询问从第a个位置到第b个位置,第C大的数是多少. Input 第一行N,M接下来M行,每行形如1 a

3110: [Zjoi2013]K大数查询 树状数组套线段树

3110: [Zjoi2013]K大数查询 Time Limit: 20 Sec  Memory Limit: 512 MBSubmit: 1384  Solved: 629[Submit][Status] 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

【BZOJ-3110】K大数查询 整体二分 + 线段树

3110: [Zjoi2013]K大数查询 Time Limit: 20 Sec  Memory Limit: 512 MBSubmit: 6265  Solved: 2060[Submit][Status][Discuss] Description 有N个位置,M个操作.操作有两种,每次操作如果是1 a b c的形式表示在第a个位置到第b个位置,每个位置加入一个数c如果是2 a b c形式,表示询问从第a个位置到第b个位置,第C大的数是多少. Input 第一行N,M接下来M行,每行形如1 a

【BZOJ3110】K大数查询(整体二分)

[BZOJ3110]K大数查询(整体二分) 题面 BZOJ 题解 看了很久整体二分 一直不知道哪里写错了 ... 又把树状数组当成线段树区间加法来用了.. 整体二分还是要想清楚在干什么: 我们考虑第\(K\)大是什么 就是还有\(K-1\)个比他小 这样子就可以考虑二分之后如何\(check\) 当前二分出一个答案之后 按照时间顺序检查每个操作 如果是添加: 如果加进去的值比二分的答案要小 证明对结果没有贡献 直接丢到左区间里不管 否则线段树做区间加法 如果是修改 检查一下当前是否满足 然后分类

【BZOJ3110】[Zjoi2013]K大数查询 树套树

[BZOJ3110][Zjoi2013]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