P3372 【模板】线段树 1

P3372 【模板】线段树 1
在这里插入图片描述

这一题是线段树懒标记的入门,懒标记的思想就是,当查询或者更改时,需要用到某段区间,那么我们就去将需要用到的区间传递下去,否者我们不动他,像不像你不用的知识不复习,用的时候再复习呢?

#include <bits/stdc++.h>
using namespace std;

typedef long long ll;
const int N = 1e5 + 10;
int w[N];
struct Node{
    ll l, r, sum, lazy; //懒标记
}tr[N << 2];

inline ll ls(ll u) {return u << 1; }
inline ll rs(ll u) {return u << 1 | 1; }

void pushup(ll u, ll l, ll r){ //更新父节点
    tr[u] = {l, r, tr[ls(u)].sum + tr[rs(u)].sum, 0};
}

void pushdown(ll u) //传递懒标记
{
    if(tr[u].lazy)  //如果懒标记不是0
    {
        ll mid = tr[u].l + tr[u].r >> 1;
        tr[ls(u)].lazy += tr[u].lazy;   //给左子树打上懒标记
        tr[ls(u)].sum  += tr[u].lazy * (mid - tr[u].l + 1); //更新左子树的和
        tr[rs(u)].lazy += tr[u].lazy;   //给右子树打上懒标记
        tr[rs(u)].sum  += tr[u].lazy * (tr[u].r - mid);     //更新左子树的和
        tr[u].lazy = 0;
    }
}

void build(ll u, ll l, ll r)
{
    if(l == r){
        tr[u] = {l, r, w[l], 0};
        return;
    }

    ll mid = l + r >> 1;
    build(ls(u), l, mid);
    build(rs(u), mid + 1, r);
    pushup(u, l, r);
}

void modify(ll u, ll l, ll r, ll lazy) //修改
{
    if(l <= tr[u].l && tr[u].r <= r)
    {
        tr[u].sum += (tr[u].r - tr[u].l + 1) * lazy;
        tr[u].lazy += lazy;
        return;
    }

    pushdown(u);     //懒标记下放
    ll mid = tr[u].l + tr[u].r >> 1;
    if(l <= mid) modify(ls(u), l, r, lazy);
    if(r > mid)  modify(rs(u), l, r, lazy);
    tr[u].sum = tr[ls(u)].sum + tr[rs(u)].sum; //更新区间和
}

ll query(ll u, ll l, ll r){
    if(l <= tr[u].l && tr[u].r <= r){
        return tr[u].sum;
    }

    pushdown(u); //向下传递懒标记
    ll mid = tr[u].l + tr[u].r >> 1, sum = 0;
    if(l <= mid) sum += query(ls(u), l, r);
    if(r > mid)  sum += query(rs(u), l, r);
    return sum;
}

int main()
{
    // freopen("in.txt", "r", stdin);
    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 op;
        scanf("%d", &op);
        if(op == 1)
        {
            int x, y, k;
            scanf("%d%d%d", &x, &y, &k);
            modify(1, x, y, k);
        }
        else
        {
            int x, y;
            scanf("%d%d", &x, &y);
            printf("%lld\n", query(1, x, y));
        }
    }

    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值