给定一个长度为 N 的数列 A,以及 M 条指令,每条指令可能是以下两种之一:
C l r d
,表示把 A[l],A[l+1],…,A[r]都加上 d。Q l r
,表示询问数列中第 l∼r 个数的和。
对于每个询问,输出一个整数表示答案。
输入格式
第一行两个整数 N,M。
第二行 N 个整数 A[i]。
接下来 M行表示 M 条指令,每条指令的格式如题目描述所示。
输出格式
对于每个询问,输出一个整数表示答案。
每个答案占一行。
数据范围
1≤N,M≤105,
|d|≤100000,
|A[i]|≤109
输入样例:
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
代码:
/*
由于modify直接修改了一些区间上面的add与sum,导致递归路径上的父节点全部都需要重新算一遍sum,所以递归结尾需要加一个pushup,
同时递归路径上如果存在带有懒标记的区间,则区间结尾的pushup会用两个子区间的返回值直接覆盖该区间的sum,但是该区间的懒标记add值还没有加到子区间上面去,也就是说懒标记的附加值就会被直接覆盖掉。
这种pushup会直接消灭懒标记,所以modify递归路径上必须全部消灭懒标记,即每一层递归都要先pushdown。
这里可以看到pushup和pushdown是一个组合,递归中每一层pushup保证了递归树的叶节点的改变对父节点的影响能够及时修正,每一层的pushdown都能保证本次的递归路径上一定不存在懒标记,保证本次递归路径上的sum赋值全部为正确值。
所以尽管查询操作并没有新增懒标记且保证递归路径上没有懒标记导致我们不需要pushup,但当我们在query的时候在每一层返回sum之前加一次pushup上述的一套组合依然能保证查询结果一定是正确的,
所以可以从这一套组合的性质出发直接 背下来,在简单运用线段树模板时候直接modify和query都用这一套,就省事很多了。
*/
#include<bits/stdc++.h>
using namespace std;
#define x first
#define y second
#define int long long
#define endl '\n'
#define IOS ios::sync_with_stdio(false); cin.tie(0);cout.tie(0);
typedef unsigned long long ull;
typedef pair<int, int> PII;
int lowbit(int x) { return x & (- x); }
int gcd(int a, int b) { return b ? gcd(b, a % b) : a; }
const int mod = 1e9 + 7;
const int N = 100010;
int n, m;
int w[N];
struct Node
{
int l, r;
int sum, add;
} tr[N * 4];
void pushup(int u)
{
tr[u].sum = tr[u << 1].sum + tr[u << 1 | 1].sum;
}
void pushdown(int u)
{
auto& root = tr[u], &left = tr[u << 1], &right = tr[u << 1 | 1];
if (root.add)
{
left.add += root.add, left.sum += (left.r - left.l + 1) * root.add;
right.add += root.add, right.sum += (right.r - right.l + 1) * root.add;
root.add = 0;
}
}
void build(int u, int l, int r)
{
if (l == r) tr[u] = {l, r, w[r], 0};
else
{
tr[u] = {l, r};
int mid = l + r >> 1;
build(u << 1, l, mid), build(u << 1 | 1, mid + 1, r);
pushup(u);
}
}
void modify(int u , int l, int r, int d)
{
if (tr[u].l >= l && tr[u].r <= r)
{
tr[u].sum += (tr[u].r - tr[u].l + 1) * d;
tr[u].add += d;
}
else
{
pushdown(u);
int mid = tr[u].l + tr[u].r >> 1;
if (l <= mid) modify(u << 1, l, r, d);
if (r > mid) modify(u << 1 | 1, l, r, d);
pushup(u);
}
}
int query(int u, int l , int r)
{
if (tr[u].l >= l && tr[u].r <= r) return tr[u].sum;
pushdown(u);
int mid = tr[u].l + tr[u].r >> 1;
int sum = 0;
if (l <= mid)
sum = query(u << 1, l, r);
if (r > mid) sum += query(u << 1 | 1, l, r);
return sum;
}
signed main()
{
IOS;
cin >> n >> m;
for (int i = 1; i <= n; i++)
cin >> w[i];
build(1, 1, n);
char op[2];
int l, r, d;
while (m--)
{
cin >> op >> l >> r;
if (op[0] == 'C')
{
cin >> d;
modify(1, l, r, d);
}
else
cout << query(1, l, r) << endl;
}
return 0;
}