码迷,mamicode.com
首页 > 其他好文 > 详细

Luogu P3372 【模板】线段树 1

时间:2020-07-26 23:17:05      阅读:81      评论:0      收藏:0      [点我收藏+]

标签:区别   std   can   bug   线段   假设   注意   info   include   

技术图片

技术图片

思路

线段树1是一道线段树的经典模板题,所涉及的线段树基础知识也比较全面,作为线段树初学者(比如我)的练手题就非常合适。这道题想让我们完成的是对一个序列的区间修改和区间查询。关于这两个操作,

我们要引入一个新的东西——lazytag。

关于线段树的一些基础写法在这里不再多赘述,我主要来讲一下有关lazytag的用法和与之相关的push_down函数。

lazytag主要是应用于区间修改的这个操作,主要就是为了在时间复杂度上进一步优化。当我们每次要修改某个区间的值,我们就可以在这个区间的节点上打一个标记,代表这个位置需要进行的修改,而不

用遍历到每一个叶子结点,节约了时间。但是,在进行更新和求值的时候,一定不能忘了先push_down,要不然会炸得很惨。

如果这样说还是难以理解,我们可以举个例子:

技术图片

我们建立如上的一颗线段树,假设我们要对2~6这个区间进行修改(一定注意更新之前先把之前的标记下传并清空)。我们从根节点开始遍历,发现根节点的左儿子包括2~6这个区间的一部分,所以我们

就向根节点的左儿子遍历;接下来我们发现1~2节点和2~4节点都包括2~6节点的部分,所以我们就分别向两边遍历;接下来我们发现3~4这个节点所维护的区间完全在我们想要更新的区间里,那么我们就

没有必要再向下遍历,直接更新3~4这个节点的tag并返回即可。对于其他点的处理也是一样的,在这里就不一一列举了。最后强调的一点就是,在我们想要进行下一次更新或者求区间和的操作时,一定

要把上一次的标记清空。这个一定要记住,否则你做题DeBug的时候会遭受非常大的折磨。

技术图片

别看在我上面举的那个例子好像和直接暴力更新区别不大,但是当你的数据量大起来的时候,这个操作就显得尤为重要。再说一遍,在更新和求值之前千万不要忘了标记下放啊!!!

Code

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#define MAXN 100010
typedef long long ll;
ll n,m,a[MAXN];
ll tree[MAXN << 2], tag[MAXN << 2];
inline int lson(ll x) {return x << 1;}
inline int rson(ll x) {return x << 1 | 1;}
inline void push_up(ll k){
    tree[k] = tree[lson(k)] + tree[rson(k)];
    return;
}
inline void push_down(ll k,ll l,ll r){
    ll mid = (l + r) >> 1;
    tag[lson(k)] += tag[k];
    tree[lson(k)] += (mid - l + 1) * tag[k];
    tag[rson(k)] += tag[k];
    tree[rson(k)] += (r - mid) * tag[k];
    tag[k] = 0;
    return;
}
void build(ll k,ll l,ll r){
    if(l==r){
        tree[k]=a[l];
        return;
    }
    ll mid = (l + r) >> 1;
    build(lson(k), l, mid);
    build(rson(k), mid + 1, r);
    push_up(k);
    return;
}
void update(ll k,ll l,ll r,ll cl,ll cr,ll v){
    if(cl<=l&&r<=cr){
        tag[k]+=v;
        tree[k] += (r - l + 1) * v;
        return;
    }
    push_down(k, l, r);
    ll mid = (l + r) >> 1;
    if(cl<=mid)
        update(lson(k), l, mid, cl, cr, v);
    if(cr>mid)
        update(rson(k), mid + 1, r, cl, cr, v);
    push_up(k);
    return;
}
ll query(int k,int l,int r,int ql,int qr){
    ll res = 0;
    if(ql<=l&&r<=qr)
        return tree[k];
    push_down(k, l, r);
    int mid = (l + r) >> 1;
    if(ql<=mid)
        res += query(lson(k), l, mid, ql, qr);
    if(qr>mid)
       res += query(rson(k), mid + 1, r, ql, qr);
    return res;
}
int main(){
    scanf("%lld%lld",&n,&m);
    for (int i = 1; i <= n;++i)
        scanf("%lld",&a[i]);
    build(1, 1, n);
    for (int i = 1; i <= m;++i){
        int opt=0;
        scanf("%d", &opt);
        if(opt==1){
            int x = 0, y = 0;
            ll k = 0;
            scanf("%d%d%lld",&x,&y,&k);
            update(1, 1, n, x, y, k);
        }
        else{
            int x=0,y=0;
            scanf("%d%d", &x, &y);
            printf("%lld\n", query(1, 1, n, x, y));
        }
    }
    return 0;
}

Luogu P3372 【模板】线段树 1

标签:区别   std   can   bug   线段   假设   注意   info   include   

原文地址:https://www.cnblogs.com/ShadowFlowhyc/p/13381755.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!