题目描述
你有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 <iostream>
#include <cmath>
using namespace std;
long long int a,b,c;
long long int ans=0;
struct node //定义线段树
{
int l,r,f; //代表这个节点所代表的区间的左右端点,懒标记
long long int w; //这个节点的权值合它的子节点的权值和
}tree[500000]; //结构体要开4倍空间
void down(int k) //懒标记下传
{
tree[k*2].f+=tree[k].f; //更改子节点的懒标记的值
tree[k*2+1].f+=tree[k].f;
tree[k*2].w+=tree[k].f*(tree[k*2].r-tree[k*2].l+1);
tree[k*2+1].w+=tree[k].f*(tree[k*2+1].r-tree[k*2+1].l+1);
tree[k].f=0; //懒标记重新定义为0,防止二次使用
}
void build (int k,int ll,int rr) //k为第几个节点,ll、rrwei区间的左右端点
{
tree[k].l=ll,tree[k].r=rr; //把节点看做是一个区间
if (rr==ll) //如果到叶子结点
{
cin>>tree[k].w;
return ; //如果是子节点,返回,不用往下继续
}
int mid=(ll+rr)/2;
build (k*2,ll,mid); //先往左走
build (k*2+1,mid+1,rr); //再往右走
tree[k].w=tree[k*2].w+tree[k*2+1].w; //如果走到叶子结点,返回之后不用走这一步
}
void add(int k)
{
if (tree[k].l>=a&&tree[k].r<=b) //如果要修改的区间中包括了这个节点所有的子节点
{
tree[k].w+=(tree[k].r-tree[k].l+1)*c; //改变节点代表的权值
tree[k].f+=c;
return ; //还有[a、l]、[r、b]没有修改,返回上一个节点,往另一边寻找
} //往后走是要修改的区间[a、b]包括在[l、r]中
if (tree[k].f) down(k); //如果这个节点的子节点被修改过,懒标记下传
int mid=(tree[k].l+tree[k].r)/2;
if (a<=mid) add(k*2); //如果a在中点的左边,往左节点找
if (b>mid) add(2*k+1); //如果b在重点的右边,往右节点找
tree[k].w=tree[k*2].w+tree[2*k+1].w;
}
void sum(int k)
{
if (tree[k].l>=a&&tree[k].r<=b)
{
ans+=tree[k].w;
return ; //因为w代表这个区间的和,不用再递归求和
}
if (tree[k].f) down(k);
int mid=(tree[k].l+tree[k].r)/2;
if (a<=mid) sum(2*k);
if (b>mid) sum(2*k+1);
}
int main ()
{
int n,q;
char s;
cin>>n>>q;
build (1,1,n); //建树
for (int i=1;i<=q;i++)
{
cin>>s;
if (s=='C')
{
cin>>a>>b>>c;
add(1);
}
if (s=='Q')
{
ans=0;
cin>>a>>b;
sum(1);
cout<<ans<<endl;
}
}
return 0;
}