题目链接:
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<iostream>
#include<string>
#include<queue>
#include<set>
#include<vector>
#include<stack>
#include<map>
#include<cmath>
#include<cstring>
#include<cstdio>
#include<algorithm>
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<<tree[1].sum<<endl;
}
else if(number==2){
scanf("%d %d",&x,&y);
printf("%lld\n",query(1,x,y));
}
}
/* cout<<tree[1].sum<<endl;
cout<<tree[2].sum<<endl;*/
}
(仅供个人理解)