POJ - 3468 A Simple Problem with Integers(区间修改(lazy) + 区间求和)

题目链接

POJ-3468

题目大意

n 个数,q个操作,操作分为两种:
Cabc ,表示将区间 [a,b] 都加上 c (10000c10000)
Qab ,表示询问区间 [a,b] 的和。

数据范围

1n,q100000
1e9ai1e9

解题思路

线段树模板,区间修改,区间求和。

学到这线段树才算刚入门,写一下这个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;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值