• [多重解法]线段树练习(动态求连续区间和,区间最大值)


    目录

    例1:

    写法一(线段树):

    写法二(ST表):

    写法三(树状数组):

    即时插点和递归查询

    建树和递推查询

    例2:

    写法一(线段树):

    写法二(树状数组): 



    线段树目标:求任意动态区间元素的最值or总和

    场景描述:

    给定了一个数组, 接下来求指定区间的最值或总和,最简单的做法便是直接遍历该区间, 但数组可能非常大或者会对其中的数再做修改,每次都暴力会超时 

    线段树满足完全二叉树的性质,能将时间复杂度降到O(\log_{2}n)级别


    例1:

    输入样例:

    10 2
    3 2 4 5 6 8 1 2 9 7
    1 4
    3 8
    

    输出样例:

    5
    8

    写法一(线段树):

    运行时间: 3125 ms

    #include <iostream>
    #include <cstring>
    #include <climits>
    #include <algorithm>
    using namespace std;
    
    const int N = 1e5+10;
    int w[N];
    struct tree{
        int l, r;
        int _max;
    }tr[4*N];
    
    //建树
    void build(int u, int l, int r)
    {
        if(l == r) //叶节点
            tr[u] = {l, r, w[l]};
        else{
            tr[u] = {l, r};
            int mid = l+r >> 1;
    
            //递归建立左右子树, 并在归来之时更新当前节点
            build(u << 1, l, mid), build(u << 1 | 1, mid+1, r);
            tr[u]._max = max(tr[u<<1]._max, tr[u<<1|1]._max);
        }
    }
    
    //查询指定区间[l, r]
    int query(int u, int l, int r)
    {
        //当前节点所代表的区间是所求区间的子集
        if(tr[u].l>=l && tr[u].r<=r) return tr[u]._max;
        
        int mid = tr[u].l+tr[u].r >> 1;
        int res = INT_MIN;
        //当前区间有一部分在当前节点所代表区间的左子区间
        if(mid >= l) res = query(u << 1, l, r);
        //当前区间有一部分在当前节点所代表区间的右子区间
        if(mid < r) res = max(res, query(u<<1|1, l, r));
        
        return res;
    }
    
    int main()
    {
        int n, m;
        scanf("%d%d", &n, &m);
        
        for(int i = 1; i <= n; i ++) scanf("%d", &w[i]);
        build(1, 1, n);
        
        while (m -- )
        {
            int l, r;
            scanf("%d%d", &l, &r);
            printf("%d\n", query(1, l, r));
        }
        return 0;
    }

    写法二(ST表):

    相关题型: 

    (ST表求区间最值)AcWing 1273. 天才的记忆_☆迷茫Dog的秘密基地☆-CSDN博客首先通过初始化,我们赋予f[i][j]如下特性:表示要查询的原数组下标i及其之后长度的区间(即下表区间[i, i+(1<https://blog.csdn.net/qq_39391544/article/details/122216413)])中的最值那么如何知晓这个区间中的最值呢?<>

    运行时间: 1402 ms

    #include<iostream>
    #include<cstdio>
    #include<cmath>
    #include<algorithm>
    using namespace std;
     
    const int N = 1e5+10, M = 18;
    int n, m;
    int f[N][M], w[N];
     
    int search(int l, int r)
    {
        int len = r-l+1;
        int k = log(len)/log(2);
        return max(f[l][k], f[r-(1<<k)+1][k]);
    }
     
    int main()
    {
        scanf("%d%d", &n, &m);
        for(int i = 1; i <= n; i ++) scanf("%d", &f[i][0]);
        for(int j = 1; j < M; j ++)
            for(int i = 1; i+(1<<j)-1 <= n; i ++)
                f[i][j] = max(f[i][j-1], f[i+(1<<j-1)][j-1]);
        
        while(m --)
        {
            int l,r;
            scanf("%d%d", &l, &r);
            printf("%d\n", search(l, r));
        }
    }

    写法三(树状数组):

    即时插点和递归查询

    #include<iostream>
    #include<algorithm>
    using namespace std;
    
    const int N = 1e5+10;
    
    int nums[N], bit[N], n, m;
    
    inline int lowbit(int x) {
        return x & -x;
    }
    
    void add(int x) {
         for (int i = x; i <= n; i += lowbit(i)) {
             bit[i] = nums[i];
            for (int j = 1; j < lowbit(i); j <<= 1)
                bit[i] = max(bit[i], bit[i - j]);
        }
    }
    
    
    int query(int l, int r) {
        if (l == r) return nums[r];
        if (l == r - lowbit(r) + 1) return bit[r];
        if (l < r - lowbit(r) + 1)  return max(bit[r], query(l, r - lowbit(r)));
        else    return max(nums[r], query(l, r - 1));
    }
    
    int main()
    {
        int l, r;
        scanf("%d %d", &n, &m);
        for (int i = 1; i <= n; i ++) {
            scanf("%d", &nums[i]);
            add(i);
        }
        while (m --) 
        {
            scanf("%d %d", &l, &r);
            printf("%d\n", query(l, r));
        }
        return 0;
    }


    建树和递推查询

    #include<iostream>
    #include<algorithm>
    using namespace std;
    const int N = 1e5+10;
    
    int nums[N], bit[N], n, m;
    
    inline int lowbit(int x) {
        return x & -x;
    }
    
    // 初始化树状数组
    void build() { 
        for (int i = 1; i <= n; ++ i) {
            bit[i] = nums[i];
            for (int j = 1; j < lowbit(i); j <<= 1)
                bit[i] = max(bit[i], bit[i - j]);
        }
    }
    
    // 区间查询
    int query(int l, int r) { 
        int maxv = INT_MIN;
        while (l <= r) {
            maxv = max(maxv, nums[r]);
            r --;
            for (; l <= r - lowbit(r); r -= lowbit(r))
                maxv = max(maxv, bit[r]);
        }
        return maxv;
    }
    
    int main()
    {
        int l, r;
        scanf("%d %d", &n, &m);
        for (int i = 1; i <= n; i ++) scanf("%d", &nums[i]);
        build();
        while (m --) {
            scanf("%d %d", &l, &r);
            printf("%d\n", query(l, r));
        }
        return 0;
    }

    例2:

    输入样例:

    10 5
    1 2 3 4 5 6 7 8 9 10
    1 1 5
    0 1 3
    0 4 8
    1 7 5
    0 4 8
    

    输出样例:

    11
    30
    35

    写法一(线段树):

    #include <iostream>
    #include <cstring>
    #include <algorithm>
    using namespace std;
    
    const int N = 1e5+10;
    int w[N];
    
    struct tree{
        int l, r;
        int sum;
    }tr[4 * N];
    
    void build(int u, int l, int r)
    {
        if (l == r) tr[u] = {l, r, w[l]};
        else
        {
            tr[u] = {l, r};
            int mid = l + r >> 1;
            build(u << 1, l, mid), build(u << 1 | 1, mid + 1, r);
            tr[u].sum = tr[u << 1].sum + tr[u << 1 | 1].sum;
        }
    }
    
    int query(int u, int l, int r)
    {
        if (tr[u].l >= l && tr[u].r <= r) return tr[u].sum;
    
        int mid = tr[u].l + tr[u].r >> 1;
        int res = 0;
        if (l <= mid) res += query(u << 1, l, r);
        if (r > mid) res += query(u << 1 | 1, l, r);
        return res;
    }
    
    void modify(int u, int x, int v)
    {
        if(tr[u].l == tr[u].r) tr[u].sum += v;
        else{
            int mid = tr[u].l + tr[u].r >> 1;
            if(x <= mid) modify(u << 1, x, v);
            else modify(u << 1 | 1, x, v);
            tr[u].sum = tr[u << 1].sum + tr[u << 1 | 1].sum;
        }
    }
    
    int main()
    {
        int n, m;
        scanf("%d%d", &n, &m);
        
        for(int i = 1; i <= n; i ++) scanf("%d", &w[i]);
        build(1, 1, n);
        
        int k, a, b;
        while (m -- )
        {
            scanf("%d%d%d", &k, &a, &b);
            if(k) modify(1, a, b);
            else printf("%d\n", query(1, a, b));
        }
        return 0;
    }

    写法二(树状数组): 

    #include <iostream>
    #include <cstring>
    #include <algorithm>
    using namespace std;
    
    const int N = 1e5+10;
    int n;
    int s[N];
    
    int lowbit(int x) {
        return x & -x;
    }
    void add(int x, int d){
        for(int i = x; i <= n; i += lowbit(i)) s[i] += d;
    }
    int search(int x){
        int res = 0;
        for(int i = x; i; i -= lowbit(i)) res += s[i];
        return res;
    }
    
    int main()
    {
        int m;
        scanf("%d%d", &n, &m);
        
        int x;
        for(int i = 1; i <= n; i ++){
            scanf("%d", &x);
            add(i, x);
        }
        int k, a, b;
        for (int i = 0; i < m; i ++ )
        {
            scanf("%d%d%d", &k, &a, &b);
            if(k) add(a, b);
            else printf("%d\n", search(b)-search(a-1));
        }
        return 0;
    }
  • 相关阅读:
    linux 磁盘挂载及查看磁盘
    【转】Linux 如何通过命令仅获取IP地址
    【转】CentOS 7 安装配置 NFS
    【转】利用virtualenv管理Python环境
    ssh 常用命令
    JavaScript 视频教程 收藏
    MySQL Json类型的数据处理
    Nhibernate + MySQL 类型映射
    ABP框架服务层的接口与实现(增删改查)
    ABP框架源码中的Linq扩展方法
  • 原文地址:https://www.cnblogs.com/Knight02/p/16006965.html
Copyright © 2020-2023  润新知