【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

Sample Output

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 }

时间: 2024-12-11 20:24:14

【BZOJ3110】【整体二分+树状数组区间修改/线段树】K大数查询的相关文章

【bzoj3110】[Zjoi2013]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 题解 整体二分+树状数组区间修改 当年naive的树套树题解 前两天由于要

树状数组 区间修改+区间查询

其实直到不久前都几乎不会用树状数组,请教了PPZ大佬之后终于懂了一点点. 然后小蒟蒻现在才知道了树状数组区间修改+区间查询的方法QAQ 传送门 Codevs 线段树练习3 //Twenty #include<cstdio> #include<cstdlib> #include<iostream> #include<algorithm> #include<cmath> #include<cstring> #include<queu

【bzoj3779】重组病毒 LCT+树上倍增+DFS序+树状数组区间修改区间查询

题目描述 给出一棵n个节点的树,每一个节点开始有一个互不相同的颜色,初始根节点为1. 定义一次感染为:将指定的一个节点到根的链上的所有节点染成一种新的颜色,代价为这条链上不同颜色的数目. 现有m次操作,每次为一下三种之一: RELEASE x:对x执行一次感染: RECENTER x:把根节点改为x,并对原来的根节点执行一次感染: REQUEST x:询问x子树中所有节点感染代价的平均值. 输入 输入的第一行包含两个整数n和m,分别代表局域网中计算机的数量,以及操作和询问的总数.接下来n-1行,

【bzoj4540】[Hnoi2016]序列 单调栈+离线+扫描线+树状数组区间修改

题目描述 给出一个序列,多次询问一个区间的所有子区间最小值之和. 输入 输入文件的第一行包含两个整数n和q,分别代表序列长度和询问数.接下来一行,包含n个整数,以空格隔开,第i个整数为ai,即序列第i个元素的值.接下来q行,每行包含两个整数l和r,代表一次询问. 输出 对于每次询问,输出一行,代表询问的答案. 样例输入 5 5 5 2 4 1 3 1 5 1 3 2 4 3 5 2 5 样例输出 28 17 11 11 17 题解 单调栈+离线+扫描线+树状数组区间修改 首先把使用单调栈找出每个

【bzoj5173】[Jsoi2014]矩形并 扫描线+二维树状数组区间修改区间查询

题目描述 JYY有N个平面坐标系中的矩形.每一个矩形的底边都平行于X轴,侧边平行于Y轴.第i个矩形的左下角坐标为(Xi,Yi),底边长为Ai,侧边长为Bi.现在JYY打算从这N个矩形中,随机选出两个不同的矩形,并计算它们的并的大小.JYY想知道,交的大小的期望是多少.换句话说即求在所有可能的选择中,两个矩形交的面积的平均大小是多大. 输入 输入一行包含一个正整数N. 接下来N行,每行4个整数,分别为Xi,Yi,Ai,Bi 2 < =  N < =  2*10^5, 0 < =  Xi,

【POJ3468】【树状数组区间修改】A Simple Problem with Integers

Description You have N integers, A1, A2, ... , AN. You need to deal with two kinds of operations. One type of operation is to add some given number to each number in a given interval. The other is to ask for the sum of numbers in a given interval. In

【学习整理】树状数组 区间修改+查询

前言:对于区间修改和区间查询这样的简单问题,打一大堆线段树确实是不划算,所以学习了区间修改+区间修查询的树状数组. 我们定义 为原数列, ,显然  . 若想要将区间 的数全部 则只需要将 , 即可. 所以,我们维护一个数组 在将区间 的数全部 则还需同时将 , . 结论: 例题:CODEVS1082 线段树练习3 http://codevs.cn/problem/1082/ #include<iostream> #include<cstdio> #include<cstdli

HDU - 1556 Color the ball (一维树状数组 + 区间修改 + 单点求值)

HDU - 1556 Color the ball Time Limit: 3000MS   Memory Limit: 32768KB   64bit IO Format: %I64d & %I64u Submit Status Description N个气球排成一排,从左到右依次编号为1,2,3....N.每次给定2个整数a b(a <= b),lele便为骑上他的"小飞鸽"牌电动车从气球a开始到气球b依次给每个气球涂一次颜色.但是N次以后lele已经忘记了第I个气

【树状数组区间修改单点查询+分组】HDU 4267 A Simple Problem with Integers

http://acm.hdu.edu.cn/showproblem.php?pid=4267 [思路] 树状数组的区间修改:在区间[a, b]内更新+x就在a的位置+x. 然后在b+1的位置-x 树状数组的单点查询:求某点a的值就是求数组中1~a的和. (i-a)%k==0把区间分隔开了,不能直接套用树状数组的区间修改单点查询 这道题的K很小,所以可以枚举k,对于每个k,建立k个树状数组,所以一共建立55棵树 所以就可以多建几棵树..然后就可以转换为成段更新了~~ [AC] 1 #include