版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/YewSpadeJ/article/details/81908483
//区间加 区间求和
#include<iostream>
#include<cstdio>
#define maxn 100005
#define ls l,m,rt<<1
#define rs l,m,rt<<1|1
long long sum[maxn<<2],lazy[maxn<<2];
long long a[maxn];//a是原数组
int n,m;
using namespace std;
void push_up(int rt)//更新节点信息,根据题意做不同更新
{
sum[rt]=sum[rt<<1]+sum[rt<<1|1];
}
void build_tree(int l,int r,int rt)
{
if(l==r)
{
sum[rt]=a[l];
return;
}
int qu=(l+r)>>1;
build_tree(l,qu,rt<<1);//更新左子区间
build_tree(qu+1,r,rt<<1|1);//更新右子区间
push_up(rt);
}
void push_down(int rt,int ln,int rn)//ln,rn为左右子数量
{
if(lazy[rt])//有lazy标记
{
lazy[rt<<1]+=lazy[rt];
lazy[rt<<1|1]+=lazy[rt];//这里由于加上一个数都是相对标记\
//所以可能有重复标记,故lazy是在原来的基础上加上现在的标记
sum[rt<<1]+=lazy[rt]*ln;
sum[rt<<1|1]+=lazy[rt]*rn;
//清除已经下推的标记
lazy[rt]=0;
}
}
void update(int L,int R,long long c,int l,int r,int rt)//LR表示需要操作的大区间
//c表示加上的值 lr表示当前小区间
{
if(L<=l&&r<=R)//本次小区间全部需要操作
{
sum[rt]+=c*(r-l+1);//可以一次加进去 但需要打上lazy标记
lazy[rt]+=c;
return;
}
int qu=(l+r)>>1;
push_down(rt,qu-l+1,r-qu);//传入左右子树的数量
if(L<=qu)update(L,R,c,l,qu,rt<<1);//这里判断是否子树与大区间有交集
if(R>qu)update(L,R,c,qu+1,r,rt<<1|1);//如果有则需要递归 进行区间操作
push_up(rt);//更新本节点信息
return;
}
long long ask(int L,int R,int l,int r,int rt)
{
if(L<=l&&r<=R)
return sum[rt];
int qu=(l+r)>>1;
push_down(rt,qu-l+1,r-qu);//下推标记寻找答案
long long ans=0;
if(L<=qu)ans+=ask(L,R,l,qu,rt<<1);
if(R>qu)ans+=ask(L,R,qu+1,r,rt<<1|1);
return ans;
}
void pret()
{
cin>>n>>m;
for(int i=1;i<=n;i++)
cin>>a[i];
build_tree(1,n,1);
return;
}
int main()
{
pret();
int ord,x,y;
long long k;
for(int i=1;i<=m;i++)
{
cin>>ord;
if(ord==1)
{
cin>>x>>y>>k;
update(x,y,k,1,n,1);
}
if(ord==2)
{
cin>>x>>y;
cout<<ask(x,y,1,n,1)<<endl;}
// for(int i=0;i<=n*2;i++)
// cout<<sum[i]<<" ";
// cout<<endl;
}
}