1082 线段树练习 3 区间修改,区间查询;
时间限制: 3 s
空间限制: 128000 KB
题目等级 : 大师 Master
给你N个数,有两种操作:
1:给区间[a,b]的所有数增加X
2:询问区间[a,b]的数的和。
输入描述 Input Description
第一行一个正整数n,接下来n行n个整数,
再接下来一个正整数Q,每行表示操作的个数,
如果第一个数是1,后接3个正整数,
表示在区间[a,b]内每个数增加X,如果是2,
表示操作2询问区间[a,b]的和是多少。
pascal选手请不要使用readln读入
输出描述 Output Description
对于每个询问输出一行一个答案
样例输入 Sample Input
3
1
2
3
2
1 2 3 2
2 2 3
样例输出 Sample Output
9
数据范围及提示 Data Size & Hint
数据范围
1<=n<=200000
1<=q<=200000
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int sz=200000+50;
struct seg_tree{
int l,r;
long long sum;
long long ax;
} t[sz<<2];
int n,a,q,x,y,z;
long long v[sz];
void updata(int m){
t[m].sum=t[m<<1].sum+t[m<<1|1].sum;
return ;
}
void build(int m,int ll,int rr){
t[m].l=ll,t[m].r=rr;
if(ll==rr) {
t[m].sum=v[ll];
return ;
}
int mid=(ll+rr)>>1;
build(m<<1,ll,mid);
build(m<<1|1,mid+1,rr);
updata(m);
}
void add(int m,int vv){
t[m].sum+=(t[m].r-t[m].l+1)*vv;
t[m].ax+=vv;
return ;
}
void spread(int m){
if(t[m].ax){
add(m<<1,t[m].ax);
add(m<<1|1,t[m].ax);
t[m].ax=0;
//这里要改为0!! 否则用while会死循环
}
return ;
}
long long ask(int m,int ll,int rr){
if(t[m].l>=ll&&t[m].r<=rr) return t[m].sum;
int mid=t[m].l+t[m].r>>1;
long long ans=0;
spread(m);
if(mid>=ll) ans+=ask(m<<1,ll,rr);
if(mid<rr) ans+=ask(m<<1|1,ll,rr);
return ans;
}
void change(int m,int ll,int rr,int vv){
if(t[m].l>=ll&&t[m].r<=rr) {
add(m,vv);return;
}
int mid=(t[m].l+t[m].r)>>1;
spread(m);
if(mid>=ll) change(m<<1,ll,rr,vv);
if(mid<rr) change(m<<1|1,ll,rr,vv);
updata(m);
return ;
}
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%lld",&v[i]);
build(1,1,n);
scanf("%d",&q);
while(q--){
scanf("%d",&a);
if(a==1){
scanf("%d%d%d",&x,&y,&z);
change(1,x,y,z);
// for(int i=1;i<=n*4;i++)
//printf("i= %d t[i].l= %d t[i].r= %d t[i].sum= %d t[i].ax= %d\n",i,t[i].l,t[i].r,t[i].sum,t[i].ax);
}
else scanf("%d%d",&x,&y),printf("%lld\n",ask(1,x,y));
}
return 0;
}