题目描述如下:
![在这里插入图片描述](https://img-blog.csdnimg.cn/20190723202836726.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2R3aDEwNDE1MTgyODI=,size_16,color_FFFFFF,t_70)
由于是一道模板题就直接给注释详细的代码。
#include<cstdio>
#include<iostream>
using namespace std;
typedef long long ll;
long long int sum=0LL;
struct node
{
ll l;
ll r;
ll f;/*
f为懒惰标记,因为比较懒,如果暂时不用该节点的子节点的话,就不改变他子节点的值 ;
但是如果以后要用的话就需要改变了,而懒惰标记就是用来检查此次是否需要改变子节点的值。
*/
ll v;
}tree[500005];
void build(ll l,ll r,ll k)
{//建立线段树
tree[k].l=l;//该区间左端点为l;
tree[k].r=r;//该区间右端点为r;
if(l==r)
{
cin>>tree[k].v;//如若分到一个点如区间[3,3],输入,并返回。
return;
}
int mid=(l+r)/2;//二分法生成树
build(l,mid,k*2);
build(mid+1,r,k*2+1);
tree[k].v=tree[k*2].v+tree[k*2+1].v;//由于是返回,所以先更改的是子节点的值。
//父节点等于两个子节点(区间)值的和
}
void passdown(ll k)
{
tree[k*2].f+=tree[k].f;//将父亲的懒惰标记给左儿子
tree[k*2+1].f+=tree[k].f;//将父亲的懒惰标记给右儿子
tree[k*2].v+=(tree[k*2].r-tree[k*2].l+1)*tree[k].f;//左儿子的值等于原值+左儿子集合元素个数乘父亲的懒惰标记。
tree[k*2+1].v+=(tree[k*2+1].r-tree[k*2+1].l+1)*tree[k].f;//右儿子同理。
tree[k].f=0;//取消父亲的懒惰标记。
}
void add(ll p,ll q,ll k,ll w)
{
if(tree[k].l>=p && tree[k].r<=q)
{
tree[k].f+=w;//如果符合小于所求区间标记懒惰标记。
tree[k].v+=(tree[k].r-tree[k].l+1)*w;//因为是给区间内所有物品加值,所以是加上w*n
return;//必须返回
}
if(tree[k].f)//如果已经找到寻找的区间之一
passdown(k);//由于之前只把懒惰标记传递到了要用点便停止,他的子节点的值并未改变所以要下传懒惰标记。
ll mid=(tree[k].l+tree[k].r)/2;//二分搜索
if(p<=mid)add(p,q,k*2,w);
if (q>mid) add(p,q,k*2+1,w);
tree[k].v=tree[k*2].v+tree[k*2+1].v;//返回时改变父节点的值。
}
void sear(ll p,ll q,ll k)
{
if(tree[k].l>=p && tree[k].r<=q)
{
sum+=tree[k].v;
return;
}
if(tree[k].f)
passdown(k);//由于之前只把懒惰标记传递到了要用点便停止,他的子节点的值并未改变所以要下传懒惰标记。
long long int mid=(tree[k].l+tree[k].r)/2;
if(p<=mid) sear(p,q,k*2);
if(q>mid) sear(p,q,k*2+1);
}
int main()
{
ll n,m,x,flag,s,e,ad;
cin>>n>>m;//输入
build(1,n,1);
for(int i=1;i<=m;i++)
{
cin>>flag>>s>>e;
if(flag==2)
{
sum=0;
sear(s,e,1);
cout<<sum<<endl;
}
else
{
cin>>ad;
add(s,e,1,ad);
}
}
return 0;
}