zoukankan      html  css  js  c++  java
  • hdu1754 I hate it线段树模板 区间最值查询

    题目链接:这道题是线段树,树状数组最基础的问题

    两种分类方式:按照更新对象和查询对象

    单点更新,区间查询;

    区间更新,单点查询;

    按照整体维护的对象:

    维护前缀和;

    维护区间最值.

    线段树模板代码

    #include<iostream>
    #include<string.h>
    #include<stdio.h>
    #include<math.h>
    #include<algorithm>
    using namespace std;
    #define re(i,n) for(int i=0;i<n;i++)
    typedef long long ll;
    const int maxn = 2 * 1e5 + 7;
    #define lson(x) x<<1,f,mid
    #define rson(x) x<<1|1,mid+1,t 
    int n, m;
    int tr[maxn << 2];
    int x, y;
    int ans;
    void build(int r, int f, int t){
        if (f == t){
            scanf("%d", &tr[r]);
            return;
        }
        int mid = (f + t) >> 1;
        build(lson(r)),build(rson(r));
        tr[r] = max(tr[r << 1], tr[r << 1 | 1]);
    }
    void query(int r, int f, int t){
        if (f >= x&&t <= y){
            ans = max(ans, tr[r]);
            return;
        }
        int mid = (f + t) >> 1;
        if (x <= mid)query(lson(r));
        if (y > mid)query(rson(r));
    }
    void update(int r, int f, int t){
        if (f == t){
            tr[r] = y;
            return;
        }
        int mid = (f + t) >> 1;
        if (x<= mid)update(lson(r));
        else update(rson(r));
        tr[r] = max(tr[r << 1], tr[r << 1 | 1]);
    }
    int main(){
        //freopen("in.txt", "r", stdin);
        while (cin >> n >> m){ 
            build(1, 1, n); 
            while (m--){
                char op[2];  scanf("%s%d%d", op,&x,&y); 
                if (op[0] == 'Q'){
                    if (x > y)swap(x, y);
                    ans = -1;
                    query(1, 1, n);
                    printf("%d
    ", ans);
                }
                else{
                    update(1, 1, n);
                }
            }
        }
        return 0;
    }

    树状数组区间最值模板代码

    #include<iostream>
    #include<stdio.h>
    #include<algorithm>
    using namespace std;
    const int maxn = 2*1e5+7;
    typedef long long ll;
    #define re(i,n) for(int i=0;i<n;i++) 
    int a[maxn], c[maxn];
    /*
    a里面存放本值,c里面维护最值
    */
    int n, q;
    int lowbit(int x){
        return x&-x;
    }
    void redo(int i){
        c[i] = i;
        for (int j = 1; j < lowbit(i); j <<= 1)
        if (a[c[i - j]]>a[c[i]])c[i] = c[i - j];
    }
    void init(){
        for (int i = 1; i <= n; i++){
            redo(i);
        }
    }
    void update(int x, int y){ 
        bool big = y > a[x];
        a[x] = y;
        int i = x;
        if (big){
            /*
            这个地方如果错写成a[c[i]]<y就会导致无法传递上去,因为一开始时就是a[c[i]]==y.
            */
            while (i<=n&&a[c[i]] <= y)c[i] = x, i += lowbit(i);
        }
        else{ 
            while (i<=n&&c[i] == x)redo(i), i += lowbit(i);
        }
    }
    int query(int l, int r){
        int ans = a[r];
        while (l<=r){ 
            while (r - lowbit(r) >= l){
                ans = max(a[c[r]], ans);
                r -= lowbit(r);
            }
            ans = max(a[r], ans);//根节点
            r--;//向下走一步
        }
        return ans;
    }
    int main(){
        freopen("in.txt", "r", stdin);
        while (scanf("%d%d", &n, &q) == 2){
            re(i, n)scanf("%d", &a[i + 1]);
            init();
            int qi = 0;
            while (q--){
                char op[2]; int x, y;
                qi++;
                scanf("%s%d%d", &op, &x, &y);
                if (op[0] == 'U'){
                    update(x, y);
                }
                else{
                    int ans = query(x, y);
                    printf("%d
    ", ans);
                }
            }
        }
        return 0;
    }

    复杂度是O(lgn*lgn).这道题数据好像有点弱,因为错误的代码也能过这道题.

    zwk线段树模板 

    zwk线段树巧妙之处在于开区间写法,对于长度为N的线段,初始化时,共N+2片叶子:1和N+2空着,2~N+1才是本体,是实实在在的数据.张昆玮说了:如果有1023个数据,那就要有2048片叶子.空间要开到(N+2)*4才行.虽然费了点空间,但编程简洁了.

    根节点(1号结点)和每层的第一个节点和最后一个节点是哨兵节点,永远都不会访问到它们.它们的存在只是方便编程.老子曰:将欲取之,必先予之.一言以蔽之,就是两端边界处的节点都是哨兵节点,这是开区间方便编程导致的.

    那么闭区间写法行不行呢?对于长度为N的线段,1~N全部用上,查询时闭区间可以转换成开区间.如果有1023个数,那就只需要开辟1024片叶子的树状数组.开区间=闭区间-两个端点.若无论如何都更新两个端点,那就会导致更新的东西有点靠下,这对于结果并没什么影响.需要注意l==r的情形.

    zwk开区间写法

     1 #include<iostream>
     2 #include<stdio.h>
     3 #include<algorithm>
     4 using namespace std;
     5 const int maxn = 2 * 1e5 + 7;
     6 typedef long long ll;
     7 #define re(i,n) for(int i=0;i<n;i++)  
     8 int a[maxn << 2];
     9 int n, q, sz;
    10 void update(int x, int v){
    11     x += sz;
    12     a[x] = v;
    13     while (x >1){
    14         x >>= 1;
    15         a[x] = max(a[x << 1], a[x << 1 | 1]);
    16     }
    17 }
    18 int query(int l, int r){
    19     l += sz - 1, r += sz + 1;
    20     int ans =0; 
    21     while (l^r ^ 1){//当两人不是兄弟时
    22         if (~l & 1){
    23             ans = max(ans, a[l ^ 1]);
    24         }
    25         if (r & 1){
    26             ans = max(ans, a[r ^ 1]);
    27         }
    28         l >>= 1, r >>= 1;
    29     }
    30     return ans;
    31 }
    32 int main(){
    33     //freopen("in.txt", "r", stdin);
    34     while (scanf("%d%d", &n, &q) == 2){
    35         sz = 1; while (sz < n+2)sz <<= 1;
    36         re(i, n){
    37             scanf("%d", &a[i + sz+1]);
    38         }
    39         for (int i = n; i <= (sz << 1); i++)a[i + sz+1] = 0;
    40         for (int i = sz - 1; i>0; i--){
    41             a[i] = max(a[i << 1], a[i << 1 | 1]);
    42         }
    43         while (q--){
    44             char op[2]; int x, y;
    45             scanf("%s%d%d", op, &x, &y);
    46             if (op[0] == 'U'){
    47                 update(x, y);
    48             }
    49             else{
    50                 int ans = query(x, y);
    51                 printf("%d
    ", ans);
    52             }
    53         }
    54     }
    55     return 0;
    56 }

     zwk闭区间写法:下面的代码有bug.能过题.

     1 #include<iostream>
     2 #include<stdio.h>
     3 #include<algorithm>
     4 using namespace std;
     5 const int maxn = 2 * 1e5 + 7;
     6 typedef long long ll;
     7 #define re(i,n) for(int i=0;i<n;i++)  
     8 int a[maxn << 2];
     9 int n, q, sz;
    10 void update(int x, int v){
    11     x += sz-1;
    12     a[x] = v;
    13     while (x >1){
    14         x >>= 1;
    15         a[x] = max(a[x << 1], a[x << 1 | 1]);
    16     }
    17 }
    18 int query(int l, int r){
    19     l += sz - 1, r += sz - 1;
    20     int ans =max(a[l],a[r]); //查询时先把两个端点给处理掉,就相当于开区间了.
    21     /*
    22     这个地方的不同决定了两种写法,我觉得没有必要开区间.闭区间预处理一下就很好.
    23     
    这个地方如果l==r,就会产生死循环.但是我这么写,这道题却过了.
    */ 24 while (l^r ^ 1){//当两人不是兄弟时 25 if (~l & 1){ 26 ans = max(ans, a[l ^ 1]); 27 } 28 if (r & 1){ 29 ans = max(ans, a[r ^ 1]); 30 } 31 l >>= 1, r >>= 1; 32 } 33 return ans; 34 } 35 void init(){ 36 sz = 1; while (sz < n)sz <<= 1; 37 re(i, n){ 38 scanf("%d", &a[i + sz]);//在1~N之间存放数据 39 } 40 for (int i = n; i <= (sz << 1); i++)a[i + sz ] = 0; 41 for (int i = sz - 1; i>0; i--){ 42 a[i] = max(a[i << 1], a[i << 1 | 1]); 43 } 44 } 45 int main(){ 46 freopen("in.txt", "r", stdin); 47 while (scanf("%d%d", &n, &q) == 2){ 48 init(); 49 while (q--){ 50 char op[2]; int x, y; 51 scanf("%s%d%d", op, &x, &y); 52 if (op[0] == 'U'){ 53 update(x, y); 54 } 55 else{ 56 int ans = query(x, y); 57 printf("%d ", ans); 58 } 59 } 60 } 61 return 0; 62 }
  • 相关阅读:
    @atcoder
    @loj
    @atcoder
    @gym
    @codeforces
    @atcoder
    @bzoj
    @loj
    Kafka常用命令
    Kafka入门介绍
  • 原文地址:https://www.cnblogs.com/weiyinfu/p/4854396.html
Copyright © 2011-2022 走看看