zoukankan      html  css  js  c++  java
  • 【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
    ", 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
    ", Ans[i]); 
    147     return 0;
    148 }
    View Code
  • 相关阅读:
    201671010109 《java程序联系》第十六周感想
    201671010109 《 java 程序设计》第十五周感想
    201671010109 《java程序设计》第十四周感想
    201671010109 《java程序设计》第十三周感想
    201671010109 《java程序设计》第十二周感想
    201671010109 《java程序设计》第十一周感想
    201671010109 2016-2017-2《java程序设计》第十周学习心得
    201671010109 2016-2017《java课程》第五周学习感想
    201971010109 2061-2017《java程序设计》第四周学习感想
    201671010107 2016-2017-2 《Java程序设计》总结
  • 原文地址:https://www.cnblogs.com/hoskey/p/4340048.html
Copyright © 2011-2022 走看看