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

目录

例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;
}

 

posted @ 2022-03-15 08:51  泥烟  阅读(52)  评论(0编辑  收藏  举报