线段树简介及代码模板

线段树是一种基于分治思想的二叉树结构,用于在区间上进行信息统计。

相对于树状数组,线段树更加通用,代码更长,功能也更加强大。

线段树可以在 O(logn) 的时间复杂度内实现单点修改、单点查询、区间修改、区间查询(区间求和,区间求最大值,区间求最小值、区间求异或 etc)等操作。

线段树的一些性质:

1、每个节点都代表一个区间

2、根节点代表的区间是整个统计范围,如 [1, N]

3、线段树的每个叶节点都代表长度为 1 的元区间 [x, x]

4、对于每个内部节点 [l, r],它的左子节点是 [l, mid],右子节点是 [mid+1, r],其中 mid = (l+r) / 2

5、因为是二叉树,所以树的深度为 logn

6、通常我们把根节点编号为1,编号为 x 的节点的左子节点为 x * 2,右子节点为 x * 2 + 1

7、当统计范围为 N 时,线段树数组要不小于 4 * N

线段树的基本操作:

1、push_up,更新和值

2、build,建树操作

3、update,区间修改

4、spread(也可以叫push_down),下传懒标记

5、query,区间查询

线段树的基本结构:

ll a[N];
struct Node {
    
    
    int l, r;
    ll sum, maxv, lazy;
    void updade(ll x) {
    
    
        sum += (r - l + 1) * x;
        maxv += x;
        lazy += x;
    }
} tree[N * 4];

更新和值:

更新 p 的两个子节点后,更新 p 的值

void push_up(int p)
{
    
    
	tree[p].sum = tree[p << 1].sum + tree[p << 1 | 1].sum;
    tree[p].maxv = max(tree[p << 1].maxv, tree[p << 1 | 1].maxv);
}

建树操作:

以 p 为根节点,l 为左端点,r 为右端点,建立一棵二叉树

void build(int p, int l, int r)
{
    
    
    tree[p].l = l, tree[p].r = r;
    tree[p].sum = tree[p].lazy = 0;
    if (l == r) {
    
    
        tree[p].sum = tree[p].maxv = a[l];
        return;
    }
    int mid = l + r >> 1;
    build(p << 1, l , mid);
    build(p << 1 | 1, mid + 1, r);
    push_up(p);
}

下传懒标记:

将节点 p 的懒标记传递下去

void spread(int p)
{
    
    
    int lazy_val = tree[p].lazy;
    if (lazy_val) {
    
    
        tree[p << 1].updade(lazy_val);
        tree[p << 1 | 1].updade(lazy_val);
        tree[p].lazy = 0;
    }
}

区间修改:

将区间 [l, r] 都加上 val

p 为当前的根节点,l、r 为当前区间的左右端点,val 为增加的值

void update(int p, int l, int r, ll val)
{
    
    
    if (l <= tree[p].l &&  tree[p].r <= r) {
    
    
        tree[p].updade(val);
    } else {
    
    
        spread(p);
        int mid = tree[p].l + tree[p].r >> 1;
        if (l <= mid) 
			update(p << 1, l, r, val);
        if (r > mid) 
			update(p << 1 | 1, l, r, val);
        push_up(p);
    }
}

区间查询操作:

查询区间 [l, r] 的和

ll query(int p, int l, int r)
{
    
    
    if (l <= tree[p].l && tree[p].r <= r) 
		return tree[p].sum;

    spread(p);
    ll ans = 0;
    int mid = tree[p].l + tree[p].r >> 1;
    if (l <= mid) 
		ans = ans + query(p << 1, l, r);
    if (r > mid) 
		ans = ans + query(p << 1 | 1, l, r);
    return ans;
}

查询区间 [l, r] 的最大值

ll query(int p, int l, int r)
{
    
    
    if (l <= tree[p].l && tree[p].r <= r) 
		return tree[p].maxv;

    spread(p);
    ll ans = -(1 << 30);
    int mid = tree[p].l + tree[p].r >> 1;
    if (l <= mid) 
		ans = max(ans, query(p << 1, l, r));
    if (r > mid) 
		ans = max(ans, query(p << 1 | 1, l, r));
    return ans;
}

学姐的代码模板:

区间求和

#include <bits/stdc++.h>
using namespace std;
const int MOD = 1e9 + 7;
const int N = 1e5 + 10;
typedef long long ll;

ll a[N];
int n;

struct Node {
    
    
    int l, r;
    ll sum, maxv, lazy;
    void updade(ll x) {
    
    
        sum += (r - l + 1) * x;
        maxv += x;
        lazy += x;
    }
} tree[N * 4];

void push_up(int p)
{
    
    
	tree[p].sum = tree[p << 1].sum + tree[p << 1 | 1].sum;
    tree[p].maxv = max(tree[p << 1].maxv, tree[p << 1 | 1].maxv);
}

void build(int p, int l, int r)
{
    
    
    tree[p].l = l, tree[p].r = r;
    tree[p].sum = tree[p].lazy = 0;
    if (l == r) {
    
    
        tree[p].sum = tree[p].maxv = a[l];
        return;
    }
    int mid = l + r >> 1;
    build(p << 1, l , mid);
    build(p << 1 | 1, mid + 1, r);
    push_up(p);
}

void spread(int p)
{
    
    
    int lazy_val = tree[p].lazy;
    if (lazy_val) {
    
    
        tree[p << 1].updade(lazy_val);
        tree[p << 1 | 1].updade(lazy_val);
        tree[p].lazy = 0;
    }
}

void update(int p, int l, int r, ll val)
{
    
    
    if (l <= tree[p].l &&  tree[p].r <= r) {
    
    
        tree[p].updade(val);
    } else {
    
    
        spread(p);
        int mid = tree[p].l + tree[p].r >> 1;
        if (l <= mid) 
			update(p << 1, l, r, val);
        if (r > mid) 
			update(p << 1 | 1, l, r, val);
        push_up(p);
    }
}

ll query(int p, int l, int r)
{
    
    
    if (l <= tree[p].l && tree[p].r <= r) 
		return tree[p].sum;

    spread(p);
    ll ans = 0;
    int mid = tree[p].l + tree[p].r >> 1;
    if (l <= mid) 
		ans = ans + query(p << 1, l, r);
    if (r > mid) 
		ans = ans + query(p << 1 | 1, l, r);
    return ans;
}

int main()
{
    
    
    int q, l, r;
    int opt;
    ll val;
    scanf("%d", &n);
    for (int i = 1; i <= n; ++i) 
		scanf("%lld", a + i);
    build(1, 1, n);
    scanf("%d", &q);
    while (q--) {
    
    
        scanf("%d", &opt);
        if (opt == 2) {
    
    
            scanf("%d %d", &l, &r);
            printf("%lld\n", query(1, l, r));
        } else {
    
    
            scanf("%d %d %lld", &l, &r, &val);
            update(1, l, r, val);
        }
    }
    return 0;
}

区间求最大值

#include <bits/stdc++.h>
using namespace std;
const int MOD = 1e9 + 7;
const int N = 1e5 + 10;
typedef long long ll;

ll a[N];
int n;

struct Node {
    
    
    int l, r;
    ll sum, maxv, lazy;
    void updade(ll x) {
    
    
        sum += (r - l + 1) * x;
        maxv += x;
        lazy += x;
    }
} tree[N * 4];

void push_up(int p)
{
    
    
	tree[p].sum = tree[p << 1].sum + tree[p << 1 | 1].sum;
    tree[p].maxv = max(tree[p << 1].maxv, tree[p << 1 | 1].maxv);
}

void build(int p, int l, int r)
{
    
    
    tree[p].l = l, tree[p].r = r;
    tree[p].sum = tree[p].lazy = 0;
    if (l == r) {
    
    
        tree[p].sum = tree[p].maxv = a[l];
        return;
    }
    int mid = l + r >> 1;
    build(p << 1, l , mid);
    build(p << 1 | 1, mid + 1, r);
    push_up(p);
}

void spread(int p)
{
    
    
    int lazy_val = tree[p].lazy;
    if (lazy_val) {
    
    
        tree[p << 1].updade(lazy_val);
        tree[p << 1 | 1].updade(lazy_val);
        tree[p].lazy = 0;
    }
}

void update(int p, int l, int r, ll val)
{
    
    
    if (l <= tree[p].l &&  tree[p].r <= r) {
    
    
        tree[p].updade(val);
    } else {
    
    
        spread(p);
        int mid = tree[p].l + tree[p].r >> 1;
        if (l <= mid) 
			update(p << 1, l, r, val);
        if (r > mid) 
			update(p << 1 | 1, l, r, val);
        push_up(p);
    }
}

ll query(int p, int l, int r)
{
    
    
    if (l <= tree[p].l && tree[p].r <= r) 
		return tree[p].maxv;

    spread(p);
    ll ans = -(1 << 30);
    int mid = tree[p].l + tree[p].r >> 1;
    if (l <= mid) 
		ans = max(ans, query(p << 1, l, r));
    if (r > mid) 
		ans = max(ans, query(p << 1 | 1, l, r));
    return ans;
}

int main()
{
    
    
    int q, l, r;
    int opt;
    ll val;
    scanf("%d", &n);
    for (int i = 1; i <= n; ++i) 
		scanf("%lld", a + i);
    build(1, 1, n);
    scanf("%d", &q);
    while (q--) {
    
    
        scanf("%d", &opt);
        if (opt == 2) {
    
    
            scanf("%d %d", &l, &r);
            printf("%lld\n", query(1, l, r));
        } else {
    
    
            scanf("%d %d %lld", &l, &r, &val);
            update(1, l, r, val);
        }
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/weixin_43772166/article/details/108879524