POJ3468线段树区间更新

又一次被线段树虐..这里主要是区间延迟更新的问题,即在查询和插入的时候才对节点进行延迟添加,大大减缓了时间。
参考博文链接

http://blog.csdn.net/metalseed/article/details/8039326
http://blog.sina.com.cn/s/blog_a2dce6b30101l8bi.html

//
//  test.cpp
//  POJ3468
//
//  Created by dan on 16/9/30.
//  Copyright © 2016年 dan. All rights reserved.
//

#include <iostream>
#include <stdio.h>
const int MAXN = 111111;
using namespace std;

struct Node{
    long long value;
    long long addnum;
} sum[MAXN<<2];

long long sav[MAXN<<2];
//本题为求和,在上行更新时更新节点值为两个孩子的值之和
void pushUp(int i){
    sum[i].value = sum[ i << 1 | 1].value + sum[i << 1].value;
}
//简历树和之前一样,递归的建立
void build(int index, int l, int r){
    sum[index].addnum = 0;
    if (l == r)
    {
        sum[index].value = sav[l];
        return ;
    }
    int m = (l + r) >> 1;
    build(2 * index, l, m);
    build(2 * index + 1, m + 1, r);
    pushUp(index);
}
//下行更新发生在查询和插入到节点的时候,对所有节点一视同仁,一起进行插入(虽然叶子节点没啥用)。首先判断当前节点是否有延迟插入的标记(即addnum属性),若有的的话,对其两个孩子同样加上该标记,m参数代表该区间元素个数,由于求和,因此区间内的每个元素都要加上addnum值,左孩子加了(m - m / 2)个,右孩子加了m / 2个,因此对应的value应该加上数量*单值。最后将标记清零。
void pushDown(int index, int m){
    long long tmp = sum[index].addnum;
    if (tmp){
        //设置左右孩子节点的标志域,因为孩子节点可能被多次延迟标记又没有向下传递
        //所以是 “+=”
        sum[index << 1].addnum += tmp;
        sum[index << 1 | 1].addnum += tmp;
        sum[index << 1].value += tmp * (m - (m >> 1));
        sum[index << 1 | 1].value += tmp * (m >> 1);
        sum[index].addnum = 0;
    }
}
//由于这里是区间更新,因此与单个稍微不同,当时当l== r时即为单值插入。操作时,对对应区间的addnum添加,同时如PushDown中所说,该区间的和
//要加上个数*单值。如果查询区间不在范围内,那么递归的进行处理,先进行pushdown,然后分别对左右孩子进行更新。
void update(int begin, int end, int addnum,int l, int r, int savPos){
    if (begin <=l && r <= end){
        sum[savPos].addnum += addnum;
        sum[savPos].value += addnum * (r - l + 1);
        return;
    }
    pushDown(savPos, r - l + 1);
    int m = (r + l) >> 1;
    if (m >= begin)
        update(begin, end, addnum, l, m, savPos << 1);
    if (m < end)
        update(begin, end, addnum, m + 1, r, savPos << 1 | 1);
    pushUp(savPos);
}
//查询和之前类似。
long long query(int begin, int end, int l, int r, int savPos){
    if (begin <=l && r <= end){
        return sum[savPos].value;
    }
    pushDown(savPos, r- l + 1);
    int m = (r + l) >> 1;
    long long res = 0;
    if (m >= begin)
        res += query(begin, end, l, m, savPos << 1);
    if (m < end)
        res += query(begin, end, m + 1, r, savPos << 1|1);
    return res;
}
int main(){
    int N, Q;
    scanf("%d%d", &N, &Q);
    for (int i = 1; i <= N; i++){
        scanf("%lld", &sav[i]);
    }
    build(1, 1, N);

    while(Q--){
        char op[2];
        int a, b, c;
        scanf("%s", op);
        if (op[0] == 'Q'){
            scanf("%d%d", &a, &b);
            printf("%lld\n", query(a, b,1, N, 1));
        }
        else{
            scanf("%d%d%d", &a, &b, &c);
            update(a, b, c, 1, N, 1);
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值