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

 

你可能感兴趣的:(树状数组)