码迷,mamicode.com
首页 > 编程语言 > 详细

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

时间:2015-03-15 18:04:38      阅读:178      评论:0      收藏:0      [点我收藏+]

标签:

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 }
View Code

 

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

标签:

原文地址:http://www.cnblogs.com/hoskey/p/4340048.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!