如题,已知一个数列,你需要进行下面两种操作:
将某区间每一个数加上 kk。
求出某区间每一个数的和。
输入格式
第一行包含两个整数 n, mn,m,分别表示该数列数字的个数和操作的总个数。
第二行包含 nn 个用空格分隔的整数,其中第 ii 个数字表示数列第 ii 项的初始值。
接下来 mm 行每行包含 33 或 44 个整数,表示一个操作,具体如下:
1 x y k:将区间 [x, y] 内每个数加上 k。
2 x y:输出区间 [x, y]内每个数的和。
输出格式
输出包含若干行整数,即为所有操作 2 的结果。
输入
5 5
1 5 4 2 3
2 2 4
1 2 3 2
2 3 4
1 1 5 1
2 1 4
输出
11
8
20
#include<iostream>
#include<cstdio>
#define MAXN 1000001
#define ll long long
using namespace std;
unsigned ll n,m,a[MAXN],ans[MAXN<<2],tag[MAXN<<2];
ll ls(ll x){//x=x*2 左节点
return x<<1;
}
ll rs(ll x){//x=x*2+1 右节点
return x<<1|1;
}
void scan(){//输入数据
cin>>n>>m;
for(ll i=1;i<=n;i++)
scanf("%lld",&a[i]);
}
void push_up(ll p){//更新节点信息,这里是求和
ans[p]=ans[ls(p)]+ans[rs(p)];
}
//build函数建立线段树
void build(ll p,ll l,ll r){//l,r]表示当前节点区间,p表示当前节点的实际存储位置
tag[p]=0;
if(l==r){ans[p]=a[l];return ;}
ll mid=(l+r)>>1;
//左右递归
build(ls(p),l,mid);
build(rs(p),mid+1,r);
//更新信息
push_up(p);
}
void f(ll p,ll l,ll r,ll k){
tag[p]=tag[p]+k;
ans[p]=ans[p]+k*(r-l+1);
}0
void push_down(ll p,ll l,ll r){
ll mid=(l+r)>>1;
f(ls(p),l,mid,tag[p]);
f(rs(p),mid+1,r,tag[p]);
tag[p]=0;
}
// 区间更新,[nl,nr]为更新范围,[l,r]为线段树范围,k为更新值
void update(ll nl,ll nr,ll l,ll r,ll p,ll k){
if(nl<=l&&r<=nr)
{
ans[p]+=k*(r-l+1);
tag[p]+=k;
return ;
}
push_down(p,l,r);
ll mid=(l+r)>>1;
if(nl<=mid)update(nl,nr,l,mid,ls(p),k);
if(nr>mid) update(nl,nr,mid+1,r,rs(p),k);
push_up(p);
}
//区间查询(求和)
ll query(ll q_x,ll q_y,ll l,ll r,ll p){//[q_x,q_y]表示操作区间,[l,r]表示当前区间,p当前节点编号
ll res=0;
if(q_x<=l&&r<=q_y) return ans[p] ; //在区间内直接返回
ll mid=(l+r)>>1;
push_down(p,l,r);
if(q_x<=mid)res+=query(q_x,q_y,l,mid,ls(p));//左子区间与[q_x,q_y]有重叠,递归
if(q_y>mid) res+=query(q_x,q_y,mid+1,r,rs(p));//右子区间与[q_x,q_y]有重叠,递归
return res;
}
int main(){
ll a1,b,c,d,e,f;
scan();//
build(1,1,n);
while(m--)
{
scanf("%lld",&a1);
switch(a1)
{
case 1:{
scanf("%lld%lld%lld",&b,&c,&d);
update(b,c,1,n,1,d);
break;
}
case 2:{
scanf("%lld%lld",&e,&f);
printf("%lld\n",query(e,f,1,n,1));
break;
}
}
}
return 0;
}```