https://www.luogu.org/problemnew/show/P3372
给你n个数,然后执行m个操作,操作分两种,对一个区间内的每个数都加上一个值,或者进行某个区间和的查询.
线段树裸题,注意lazy标记的使用;
对于lazy的个人理解,当你进行更新或者操作之时,你需要只要到你需要进行操作的区间就行,当你进行给一个区间进行加值的操作,如果没有lazy,你会递归到树的叶子节点。而使用lazy之后,你只需要到那一个或者两个包含你需要操作的区间的节点,将该节点的sum加上区间长度乘以这个你要加的值,这是更新sum,但是它的子节点,即那些小区间的sum并没有改变,于是就有了lazy,到了这一个或者两个需要更新的节点,然后并不继续向下,而是将这个需要加的值加到lazy之上,等到下一次我需要进行访问它的子节点的时候,在把它传下去;其实就是访问到刚好需要操作的区间,等待下一次操作访问到,在继续进行修改。
如果我们在一次修改指令中发现节点 p p p代表的区间[ p p p r r r, p p p l l l]被修改区间 [ l , r ] [l,r] [l,r]完全覆盖,并且逐一更新了子树 p p p中的所有节点,但是在之后的查询指令中却根本没有用到 [ l , r ] [l,r] [l,r]的子区间作为候选答案,那么更新 p p p的整颗子树就是徒劳的。
换言之,我们在执行修改指令时,同样可以在 l < = l<= l<= p p p r r r < = <= <= p p p r r r < = r <=r <=r的情况下立即返回,只不过在回溯之前向节点p增加一个标记,标识 " " "该节点曾经被修改,但其子节点尚未被更新 " " "。
然后在后续的指令中,需要从节点 p p p向下递归,我们再检查 p p p是否具有标记。若有标记,就根据标记信息更新 p p p的两个子节点,同时为 p p p的两个子节点增加标记,然后清除标记。
就是说,除了在修改指令中直接划分成的 O ( l o g N ) O(logN) O(logN)个节点之外,对任意节点的修改都延迟到 " " "在后续操作递归进入他的父节点时 " " "再执行。这样对每条查询或修改指令的时间复杂度都降低到了 O ( l o g N ) O(logN) O(logN)。这些标记即为 l a z y lazy lazy标记。
(有些题目会出现对多种对区间修改的操作,例如同时出现乘法、加法、减法,这样就不仅仅是一个 l a z y lazy lazy了)
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
const double inf=0x7f7f7f7f;
const int maxn=1e5+50;
const int N=2e4+50;
typedef long long ll;
typedef struct{
int u,v,next,lca;
}Edge;
Edge e[2*maxn];
typedef struct B{
int l,r;
ll sum,lazy;
void update(int value){
sum+=(r-l+1)*value;
lazy+=value;
}
}Tree;
Tree tree[4*maxn];
int cnt,head[maxn];
void add(int u,int v){
e[cnt].u=u;
e[cnt].v=v;
/*e[cnt].w=w;
e[cnt].f=f;*/
e[cnt].next=head[u];
head[u]=cnt++;
e[cnt].u=v;
e[cnt].v=u;
/* e[cnt].w=0;
e[cnt].f=-f;*/
e[cnt].next=head[v];
head[v]=cnt++;
}
int read()
{
int x = 0;
int f = 1;
char c = getchar();
while (c<'0' || c>'9')
{
if (c == '-')
f = -1;
c = getchar();
}
while (c >= '0'&&c <= '9')
{
x = x * 10 + c - '0';
c = getchar();
}
return x*f;
}
int n,m,x,y,k,p;
void push_up(int x){
tree[x].sum=tree[x<<1].sum+tree[x<<1|1].sum;
}
void push_down(int x){
tree[x<<1].update(tree[x].lazy);
tree[x<<1|1].update(tree[x].lazy);
tree[x].lazy=0;
}
void build(int x,int l,int r){
tree[x].l=l,tree[x].r=r,tree[x].sum=0;
if(l==r){
scanf("%lld",&tree[x].sum);
return ;
}
int mid=(l+r)/2;
build(x<<1,l,mid);
build(x<<1|1,mid+1,r);
push_up(x);
}
void update(int x,int l,int r,int k){
int left=tree[x].l,right=tree[x].r;
if(l<=left&&r>=right){
tree[x].update(k);
return ;
}
push_down(x);
int mid=(left+right)/2;
if(l<=mid)update(x<<1,l,r,k);
if(r>mid)update(x<<1|1,l,r,k);
push_up(x);
}
ll query(int x,int l,int r){
int left=tree[x].l,right=tree[x].r;
int mid=(left+right)/2;
ll ans=0;
if(l<=left&&r>=right){
ans+=tree[x].sum;
}
else{
push_down(x);
if(l<=mid)ans+=query(x<<1,l,r);
if(r>mid)ans+=query(x<<1|1,l,r);
push_up(x);
}
return ans;
}
int main() {
cin>>n>>m;
build(1,1,n);
int number;
for(int i=0;i<m;i++){
scanf("%d",&number);
if(number==1){
scanf("%d %d %d",&x,&y,&k);
update(1,x,y,k);
//cout<
}
else if(number==2){
scanf("%d %d",&x,&y);
printf("%lld\n",query(1,x,y));
}
}
/* cout<
}
(仅供个人理解)