题目描述
你有N个整数,A1,A2,...,AN。 你需要处理两种操作。 一种操作是在给定间隔中为每个数字添加一些给定数字。 另一种是要求给定间隔中的数字总和。
输入
第一行包含两个数字N和Q.1≤N,Q≤100000。
第二行包含N个数字,A1,A2,...,AN的初始值。 -1000000000≤AI≤1000000000。
接下来的Q行中的每一行代表一个操作。
“C a b c”表示将C添加到Aa,Aa + 1,...,Ab中的每一个。 -10000≤c≤10000。
“Q a b”表示查询Aa,Aa + 1,...,Ab的总和。
输出
你需要回到Q个询问,每个询问一行。
样例输入
10 5 1 2 3 4 5 6 7 8 9 10 Q 4 4 Q 1 10 Q 2 4 C 3 6 3 Q 2 4
样例输出
4 55 9 15
提示
总和可能超过32位整数的范围。
思路:区间修改区间查询的线段树板子题,依旧要注意输入输出。
代码:
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn = 1e5+10;
ll sum[maxn*4],a[maxn],lazy[maxn*4];
ll n,m;
void build(ll l,ll r,ll root)
{
//赋根节点的sum初值
lazy[root]=0;
//退出递归的条件
if(l==r){
sum[root]=a[l];
return;
}
//建立两个子节点
ll mid = (l+r)/2;
build(l,mid,root*2);
build(mid+1,r,root*2+1);
//父结点等于两个子节点之和
sum[root]=sum[root*2]+sum[root*2+1];
return;
}
void push_down(int root,int len)
{
//更新两节点的root值
lazy[root*2]+=lazy[root];
lazy[root*2+1]+=lazy[root];
//更新两节点的sum值
sum[root*2]+=(len-len/2)*lazy[root];
sum[root*2+1]+=(len/2)*lazy[root];
//由于root的lazy已经被向下传递过了,所以将其置0
lazy[root]=0;
}
void update(ll x,ll y,ll num,ll l, ll r,ll root)
{
//遍历区间全都需要加上num
if(l>=x&&r<=y)
{
//标记整个区间需要加上c
lazy[root]+=num;
//先将当前节点的值更新完毕
sum[root]+=(r-l+1)*num;
return;
}
if(lazy[root])push_down(root,r-l+1);
ll mid = (l+r)/2;
//左边子节点需要更新
if(mid>=x)update(x,y,num,l,mid,root*2);
//右边子节点需要更新
if(y>mid)update(x,y,num,mid+1,r,root*2+1);
sum[root]=sum[root*2]+sum[root*2+1];
return;
}
ll query(ll x,ll y,ll l,ll r,ll root)
{
//如果整个区间被需要,则直接返回其值
if(x<=l&&y>=r)return sum[root];
//如果这个区间需要全部加上lazy[root]
//那么将这个值向下推一层
if(lazy[root]!=0) push_down(root,r-l+1);
ll ans=0,mid=(l+r)/2;
//如果左节点有被需要的节点
if(x<=mid) ans+=query(x,y,l,mid,root*2);
//如果右节点有被需要的节点
if(y>mid) ans+=query(x,y,mid+1,r,root*2+1);
return ans;
}
int main()
{
ll b,c,d;
char k;
scanf("%lld %lld",&n,&m);
for(int i = 1; i <= n; i++)
scanf("%lld",&a[i]);
build(1,n,1);
while(m--)
{
scanf(" %c ",&k);
//加数
if(k=='C')
{
scanf("%lld %lld %lld",&b,&c,&d);
update(b,c,d,1,n,1);
}
//查询
else
{
scanf("%lld %lld",&b,&c);
printf("%lld\n",query(b,c,1,n,1));
}
}
return 0;
}