题目链接——一棵普通的线段树
出题人明天就要半期考试了,课程是《火葬场与波》. 出题人倒在血泊中,一双有力的手摇晃着出题人的肩膀: “同志,醒醒,你还有题没出完呢”. 以下是他的遗言:
给你一个数组 A[1..n]A[1..n],初始值全为 0. 你需要写一棵裸的区间修改、区间查询的线段树, 以支持两个操作. 第一个操作是对区间 [L,R][L,R] 内的数每个数加上 vv. 第二个操作是给出区间 [L,R][L,R] 内所有数的和.
第一行包含两个整数 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.
对于每个 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 |
#include
#include
#include
#include
using namespace std;
const int INF=0x7f7f7f7f;
int n,m,t;
struct node
{
long long val;
int tag;
}b[4000005];
void buildb(int l,int r,int rt)
{
if(l==r)
{
b[rt].val=0;
b[rt].tag=0;
return ;
}
int mid=(l+r)>>1;
buildb(l,mid,rt*2);
buildb(mid+1,r,rt*2+1);
b[rt].val=b[rt*2].val+b[rt*2+1].val;
}
void down(int l,int r,int rt)
{
if(b[rt].tag)
{
int mid=(l+r)>>1;
b[rt*2].tag+=b[rt].tag;
b[rt*2+1].tag+=b[rt].tag;//左右子节点添加标记
b[rt*2].val+=b[rt].tag*(mid-l+1);
b[rt*2+1].val+=b[rt].tag*(r-mid);
b[rt].tag=0;//清除rt的标记
}
}
void change(int l,int r,int rt,int ll,int rr,int v)
{
if(ll<=l&&r<=rr)
{
b[rt].val+=(r-l+1)*v;
b[rt].tag+=v; //区间刚好,给节点打上延迟标记
return ;
}
down(l,r,rt);//分割区间,向下传递延迟标记
int mid=(l+r)>>1;
if(ll<=mid) change(l,mid,rt*2,ll,rr,v);
if(rr>mid) change(mid+1,r,rt*2+1,ll,rr,v);
b[rt].val=b[rt*2].val+b[rt*2+1].val;
}
long long sum(int l,int r,int rt,int ll,int rr)
{
if(ll<=l&&r<=rr) return b[rt].val;
down(l,r,rt);
int mid=(l+r)>>1;
long long cnt=0;
if(ll<=mid) cnt+=sum(l,mid,rt*2,ll,rr);
if(rr>mid) cnt+=sum(mid+1,r,rt*2+1,ll,rr);
return cnt;
}
int main()
{
while(scanf("%d%d",&n,&m)!=EOF)
{
int o,l,r,v;
memset(b,0,sizeof b);
while(m--)
{
scanf("%d%d%d%d",&o,&l,&r,&v);
if(o==0)
{
change(1,n,1,l,r,v);
}
else
{
cout<