题目描述
如题,已知一个数列,你需要进行下面两种操作:
- 将某区间每一个数加上 kk。
- 求出某区间每一个数的和。
输入格式
第一行包含两个整数 n, mn,m,分别表示该数列数字的个数和操作的总个数。
第二行包含 nn 个用空格分隔的整数,其中第 ii 个数字表示数列第 ii 项的初始值。
接下来 mm 行每行包含 33 或 44 个整数,表示一个操作,具体如下:
1 x y k
:将区间 [x, y][x,y] 内每个数加上 kk。2 x y
:输出区间 [x, y][x,y] 内每个数的和。
输出格式
输出包含若干行整数,即为所有操作 2 的结果。
输入输出样例
输入 #1复制
5 5 1 5 4 2 3 2 2 4 1 2 3 2 2 3 4 1 1 5 1 2 1 4
输出 #1复制
11 8 20
说明/提示
对于 30\%30% 的数据:n \le 8n≤8,m \le 10m≤10。
对于 70\%70% 的数据:n \le {10}^3n≤103,m \le {10}^4m≤104。
对于 100\%100% 的数据:1 \le n, m \le {10}^51≤n,m≤105。
保证任意时刻数列中任意元素的和在 [-2^{63}, 2^{63})[−263,263) 内。
【样例解释】
:
#include <bits/stdc++.h>
typedef long long ll;
const ll N=1e5+9;
ll a[N];
ll d[4*N]; //节点数设为4*N;
ll b[4*N];
inline ll read(){
ll f=1;ll res=0; char c;
c=getchar();
while(c<'0'||c>'9'){
if(c=='-')f=-1;
c=getchar();
}
while(c<='9'&&c>='0'){
res=(res<<1)+(res<<3)+c-'0';
c=getchar();
}
return f*res;
}
void build(ll s, ll t,ll p){ //建树,当前节点p区间[s,t]
if(s==t){
d[p]=a[s];
return ;
}
ll m=s+((t-s)>>1);
build(s,m,p*2);
build(m+1,t,p*2+1);
d[p]=d[p*2]+d[p*2+1];
return ;
}
void update(ll l,ll r,ll c,ll s,ll t,ll p){ //其实update和getsum是类似操作,都是找目标区间
if(l<=s&&r>=t){ //如果该节点p所在区间[s,t]包含在目标区间,则更新,并设置跟更新懒惰标记
d[p]+=c*(t-s+1);b[p]+=c;
return ;//别忘记return
}
ll m=s+((t-s)>>1);
if(b[p]){
d[p*2]+=b[p]*(m-s+1);d[p*2+1]+=b[p]*(t-m); //注意是+=
b[p*2]+=b[p];b[p*2+1]+=b[p];//注意是+=,可能父节点和子节点都有懒惰标记
b[p]=0; //撤销懒惰标记
}
if(l<=m)update(l,r,c,s,m,p*2);
if(r>m)update(l,r,c,m+1,t,p*2+1);
d[p]=d[p*2]+d[p*2+1];
return ;
}
ll getsum(ll l,ll r,ll s,ll t,ll p){
if(l<=s&&r>=t)return d[p];
ll m=s+((t-s)>>1);
if(b[p]){
d[p*2]+=b[p]*(m-s+1);d[p*2+1]+=b[p]*(t-m);
b[p*2]+=b[p];b[p*2+1]+=b[p];
b[p]=0;
}
ll sum=0;
if(l<=m)sum=getsum(l,r,s,m,p*2);
if(r>m)sum+=getsum(l,r,m+1,t,p*2+1);
return sum;
}
int main (){
ll n,m;
n=read();m=read();
ll i;
for(i=1;i<=n;i++){
a[i]=read();
}
build(1,n,1);
for(i=1;i<=m;i++){
ll check=read();
ll x,y,k;
if(check==1){
x=read();y=read();k=read();
update(x,y,k,1,n,1);
}else{
x=read();y=read();
ll res=getsum(x,y,1,n,1);
std::cout<<res<<std::endl;
}
}
return 0;
}