#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<cstring>
#include<string>
#include<cmath>
#pragma warning(disable:4996)
using namespace std;
const int N = 1e5 + 10;
typedef long long ll;
ll sum[N<<2], add[N<<2];//sum是子节点总和 add是懒惰标记,每个数值需要加上多少.
struct NODE {
int l, r;
int mid() {
return (l + r) >> 1;//在结构体中直接求出二者的mid
}
}tree[N<<2];
void PushUp(int rt)
{
sum[rt] = sum[rt << 1] + sum[rt << 1 | 1];
}
void PushDown(int rt,int m)//把lazy标记打进去 m是区间长度
{
if (add[rt])
{
add[rt << 1] += add[rt];
add[rt << 1 | 1] += add[rt];
sum[rt << 1] += add[rt] * (m - (m >> 1));
sum[rt << 1 | 1] += add[rt] * (m >>1);
add[rt] = 0;
}
}
void BuildTree(int l,int r,int rt)
{
tree[rt].l = l;
tree[rt].r = r;
add[rt] = 0;
if (l == r)
{
scanf("%I64d",&sum[rt]);
return;
}
int m = tree[rt].mid();
BuildTree(l, m, rt<<1);
BuildTree(m+1, r, rt<<1|1);
PushUp(rt);
}
void UpdateTree(int c,int l,int r ,int rt )//c是懒惰标记 rt当前节点 l,r是要更新的区间
{//区间更新
if (tree[rt].l == l && tree[rt].r == r)
{
add[rt] += c;
sum[rt] += (ll)c * (r - l+1);
return;
}
if (tree[rt].l == tree[rt].r)return;
PushDown(rt, tree[rt].r - tree[rt].l + 1);
int m = tree[rt].mid();
if (r <= m)UpdateTree(c, l, r, rt << 1);
else if (l > m)UpdateTree(c, l, r, rt << 1 | 1);
else
{
UpdateTree(c, l, m, rt << 1);
UpdateTree(c, m + 1, r, rt << 1 | 1);
}
PushUp(rt);
}
ll query(int l,int r,int rt)
{
if (tree[rt].l == l && tree[rt].r == r)
return sum[rt];
PushDown(rt,tree[rt].r-tree[rt].l+1);//记得把之前打的懒惰标记 传下去
int m = tree[rt].mid();
ll res = 0;
if (r <= m)res += query(l, r, rt << 1);
else if (l > m)res += query(l, r, rt << 1 | 1);
else
{
res += query(l, m , rt << 1 );
res+= query(m+1, r, rt << 1 | 1);
}
return res;
}
int main()
{
int q, n;
int a, b,c;
ll ans;
while (scanf("%d%d",&n,&q)!=EOF)
{
BuildTree(1,n,1);
while (q--)
{
char ch[3];
scanf("%s", ch);
if (ch[0] == 'Q')
{
scanf("%d%d",&a,&b);
ans = query(a, b, 1);
printf("%lld\n",ans);
}
else if (ch[0] == 'C')
{
scanf("%d%d%d",&a,&b,&c);
UpdateTree( c, a, b, 1);
}
}
}
return 0;
}
懒惰标记(避免更新速度太慢)
:
在增加时,如果要加的区间正好覆盖一个 节点,则增加其节点的Inc值,不再往下走 ,否则要更新nSum(加上本次增量),再将增 量往下传。这样更新的复杂度就是O(log(n))
在查询时,如果待查区间不是正好覆盖一 个节点,就将节点的Inc往下带,然后将Inc 代表的所有增量累加到nSum上后将Inc清0 ,接下来再往下查询。 一边查询,一边Inc往 下带的过程也是区间分解的过程,复杂度也是 O(log(n))