B – 一棵普通的线段树
Time Limit: 4000 MS Memory Limit: 256 MB
Submit Status
出题人明天就要半期考试了,课程是《火葬场与波》. 出题人倒在血泊中,一双有力的手摇晃着出题人的肩膀: “同志,醒醒,你还有题没出完呢”. 以下是他的遗言:
给你一个数组 A[1..n]A[1..n],初始值全为 0. 你需要写一棵裸的区间修改、区间查询的线段树, 以支持两个操作. 第一个操作是对区间 [L,R][L,R] 内的数每个数加上 vv. 第二个操作是给出区间 [L,R][L,R] 内所有数的和.
Input
第一行包含两个整数 n(1≤n≤106)n(1≤n≤106) 和 m(1≤m≤106)m(1≤m≤106), 分别是数组的大小和操作的个数.
接下来 mm 行,每行四个用空格分隔的整数 o l r v (1≤l≤r≤n,|v|≤103)o l r v (1≤l≤r≤n,|v|≤103). 如果 o=0o=0,则表示对区间 [l,r][l,r] 内每个数都加上 vv. 否则,请给出区间 [l,r][l,r] 内所有数的和,此时 v≡0v≡0.
Output
对于每个 o≠0o≠0 的操作, 输出包含一个整数的一行,表示对应区间内所有数的和.
Sample Input | Sample Output |
---|---|
5 4 0 2 4 5 1 3 5 0 0 1 3 -2 1 1 5 0 | 10 9 |
Sample input and output
#include <cstdio>
#include <iostream>
#include <cstring>
#include <cstring>
#include <algorithm>
using namespace std;
const int MAX_N = 100010;
int s[MAX_N<<2],col[MAX_N<<2];
void up(int p){
s[p] = s[p*2] + s[p*2+1];
}
void build(int p,int l,int r){
if(l==r){
s[p] = 0;
col[p] = 0;
return ;
}
int mid =(l+r)>>1;
build(p*2,l,mid);
build(p*2+1,mid+1,r);
up(p);
}
void down(int p,int l,int r){
if(col[p]){
int mid =(l+r)>>1;
col[p*2]+=col[p];
col[p*2+1]+=col[p];
s[p*2] += col[p]*(mid-l+1);
s[p*2+1] += col[p]*(r-mid);
col[p] = 0;
return ;
}
}
void change(int p,int l,int r,int x,int y,int v){
if(x<=l&&r<=y){
col[p] += v;
s[p]+=(r-l+1)*v;
return;
}
int mid = (l+r)>>1;
down(p,l,r);
if(x<=mid) change(p*2,l,mid,x,y,v);
if(y>mid) change(p*2+1,mid+1,r,x,y,v);
up(p);
}
int query(int p,int l,int r,int x,int y){
if(x<=l&&r<=y){
return s[p];
}
down(p,l,r);
int mid = (l+r)>>1,res = 0;
if(x<=mid) res+=query(p*2,l,mid,x,y);
if(y>mid) res+=query(p*2+1,mid+1,r,x,y);
return res;
}
int main(){
int n,m;
scanf("%d%d",&n,&m);
build(1,1,n);
while(m--){
int a,b,c,v;
scanf("%d%d%d%d",&a,&b,&c,&v);
if(a==0){
change(1,1,n,b,c,v);
}
else if(a==1){
printf("%d\n",query(1,1,n,b,c));
}
}
return 0;
}