题目链接:
题目大意:
n
个数,
“Cabc”
,表示将区间
[a,b]
都加上
c
“Qab”
,表示询问区间
[a,b]
的和。
数据范围:
1≤n,q≤100000
−1e9≤ai≤1e9
解题思路:
线段树模板,区间修改,区间求和。
学到这线段树才算刚入门,写一下这个lazy思想。有句话说得好:偷懒促进社会进步。lazy思想也差不多。
lazy思想主要体现在“要”的时候才去“拿”,体现在代码上就是update和query操作稍微修改了一下。
这是区间修改,如果再像单点修改那样,每次递归到最底层,更新之后返回,那还不如直接暴力改!如果要更新的区间完全包含当前节点所管辖的区间,那就直接在当前节点上更新就完了,(其实一般查询的时候也用到了类似的思想),像这样:
if(L <= tree[rt].l && tree[rt].r <= R) {
tree[rt].sum += (LL)(tree[rt].r - tree[rt].l + 1) * val;
lazy[rt] += (LL)val;
return ;
}
如果不完全包含,则将lazy标记下放到左右儿子上,即push_down操作,并将当前节点的lazy标记清零。
详见代码
AC代码:
//线段树--区间修改(Lazy标记),区间求和
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <algorithm>
#include <set>
#include <map>
#include <queue>
using namespace std;
typedef long long LL;
const int inf = 1 << 30;
const LL INF = 1LL << 60;
const int MaxN = 100010;
//这里MaxN = 100000死活过不了!!!
int n, q;
LL a[MaxN + 5];
struct segtree
{
LL l, r;
LL sum;
}tree[4 * MaxN + 5];
LL lazy[4 * MaxN + 5];
//lazy[i]表示i节点所管辖的区间应该加的值
void push_up(LL rt) {
tree[rt].sum = tree[rt << 1].sum + tree[rt << 1 | 1].sum;
}
void push_down(LL rt)
{
//理解了lazy的含义,这里就应该很好理解
if(lazy[rt] ) {
tree[rt << 1].sum += (tree[rt << 1].r - tree[rt << 1].l + 1) * lazy[rt];
tree[rt << 1 | 1].sum += (tree[rt << 1 | 1].r - tree[rt << 1 | 1].l + 1) * lazy[rt];
lazy[rt << 1] += lazy[rt];
lazy[rt << 1 | 1] += lazy[rt];
lazy[rt] = 0; //完成下放之后应该将当前节点的lazy标记清零
}
}
void Build(LL rt, LL l, LL r)
{
tree[rt].l = l, tree[rt].r = r;
if(l == r) {
tree[rt].sum = a[l];
return;
}
LL mid = (l + r) >> 1;
Build(rt << 1, l, mid);
Build(rt << 1 | 1, mid + 1, r);
//tree[rt].sum = tree[rt << 1].sum + tree[rt << 1 | 1].sum;
push_up(rt);
}
void update(LL rt, LL L, LL R, LL val)
{
//如果要更新的区间包含当前节点所代表的区间,
//则直接更新这个节点,不再往下更新,并给当前节点打上lazy标记
if(L <= tree[rt].l && tree[rt].r <= R) {
tree[rt].sum += (LL)(tree[rt].r - tree[rt].l + 1) * val;
lazy[rt] += (LL)val;
return ;
}
//否则,下放lazy标记 并更新左右儿子
push_down(rt);
LL mid = (tree[rt].l + tree[rt].r) >> 1;
if(L <= mid)
update(rt << 1, L, R, val);
if(R > mid)
update(rt << 1 | 1, L, R, val);
//tree[rt].sum = tree[rt << 1].sum + tree[rt << 1 | 1].sum;
push_up(rt);
}
LL query(LL rt, LL L, LL R)
{
//若要查询的区间完全包含当前节点的区间,则直接返回当前节点的sum
if(L <= tree[rt].l && tree[rt].r <= R)
return tree[rt].sum;
//否则,先下放lazy标记,再进行 左右儿子的查询
push_down(rt);
LL mid = (tree[rt].l + tree[rt].r) >> 1;
LL res = 0;
if(L <= mid) res += query(rt << 1, L, R);
if(R > mid) res += query(rt << 1 | 1, L, R);
return res;
}
int main()
{
while(scanf("%d %d", &n, &q) != EOF)
{
for(int i = 1; i <= n; i++) scanf("%lld", &a[i]);
Build(1, 1, n);
for(int i = 1; i <= q; i++) {
char c;
scanf(" %c", &c);
if(c == 'Q') {
LL l, r;
scanf("%lld %lld", &l, &r);
LL ans = query(1, l, r);
printf("%lld\n", ans);
}
else if(c == 'C') {
LL l, r;
LL val;
scanf("%lld %lld %lld", &l, &r, &val);
update(1, l, r, val); //区间更新
}
}
}
return 0;
}