Luogu P3372 模板线段树 1
Posted shadowflowhyc
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Luogu P3372 模板线段树 1相关的知识,希望对你有一定的参考价值。
思路
线段树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
", query(1, 1, n, x, y));
}
}
return 0;
}
以上是关于Luogu P3372 模板线段树 1的主要内容,如果未能解决你的问题,请参考以下文章