区间更新POJ3468 HDU1698

网址https://vjudge.net/problem/POJ-3468
最近学了线段树,搞了一下区间更新。模板题吧。
写一下自己需要记住的点吧。
1.空间要为节点数的4倍,我在网上找了一下,大概意思是这样
建树后可能出现这种情况这里写图片描述(图片来自http://scinart.github.io/acm/2014/03/19/acm-segment-tree-space-analysis/
结果在最下面的一行原本没完全使用却占用了空间,占用的是比这个图刚刚大的完美二叉树空间。
计算一下此图空间:((n-1)*2-1+1)*2-1=4n-5
n-1倒数第二行空间。*2-1,,除去最后一行的总空间,,+1最后一行空间。
2.类型要为long long int 类型,因为最后会超int范围
3.被标记的点已经更新完信息。等待向下更新标记。
4.延迟标记。
思想:对于要更新的区段,将其记录在祖先节点,而不完全将祖先节点记录整个区间
的子孙节点信息更新。当需要使用其下的子孙节点时再向下更新信息。这种方式大大减少了
时间复杂度。
方式:修改与查询时,遇到节点,查看其是否被标记。是的话就向下传递标记,更新子节点信息,并消除父节点标记。
5.建立的图如下。。。(好丑)
这里写图片描述
6.具体地方在里面备注

#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
/******************************************************/
#define LL long long int
#define mem(a,b) memset(a,b,sizeof(a))
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define L rt<<1
#define R rt<<1|1
#define N 100000+9
#define pow(a) a*a
#define INF 0x3f3f3f3f
#define max(a,b) (a>b?a:b)
#define min(a,b) (a<b?a:b)
/*********************************************************/
LL n, q;
LL dat[N<<2];
LL sum[N<<2];
LL addmark[N<<2];
void pushup(LL rt){
    sum[rt] = sum[L] + sum[R];
}
void pushdown(LL rt, LL len){
    if (addmark[rt])
    {
        addmark[L] += addmark[rt];
        addmark[R] += addmark[rt];

        sum[L] += addmark[rt]*(len - (len>>1));//这里要用rt,不用L。加更改后的值
        sum[R] += addmark[rt]*(len>>1);
        addmark[rt] = 0;
    }
}
void build(LL l, LL r, LL rt){
    addmark[rt] = 0;
    if (l == r){
        scanf("%lld", &sum[rt]);
        return;
    }
    LL m = (l + r) / 2;
    build(lson);
    build(rson);
    pushup(rt);
}
LL query(LL a,LL b,LL l, LL r, LL rt){
    if (a <= l&&r <= b){
        return sum[rt];
    }
    LL m = (l + r) / 2;
    LL res = 0;
    pushdown(rt, r - l + 1);
    if (a <= m)res+=query(a, b, lson);
    if (b>m)res+=query(a, b, rson);
    return res;
}

void update(LL a, LL b, LL l, LL r, LL rt, LL c){
    if (a <= l&&r <= b){
        addmark[rt] += c;   
        sum[rt] += c * (r - l + 1);
        return;
    }
    pushdown(rt, r-l+1);    //向下传递标记,更新左右儿子信息
    LL m = (l + r) / 2;
    if(a<=m)update(a, b, lson, c);
    if(b>m)update(a, b, rson, c);
    pushup(rt);    //向上更新
}
int main(){
    while (~scanf("%lld%lld", &n, &q))
    {
        build(1, n, 1);
        while (q--){
            char s[2];
            scanf("%s", s);
            if (s[0] == 'Q'){
                LL a, b;
                scanf("%lld%lld", &a, &b);
                LL res = query(a, b, 1, n, 1);
                printf("%lld\n", res);
            }
            else{
                LL a, b, c;
                scanf("%lld%lld%lld", &a, &b, &c);
                update(a, b, 1, n, 1, c);
            }
        }
    }
}

HDU1689区间更新

更新的时候,从顶节点一路往下推。当遇到标记时,将标记向下传递。因为在某区间段更新是在递归最后实现的,所以之前的传递造成的标记会被更新的标记覆盖。更新结束之后,要实现求和的功能。

求和过程也是一路从上往下找。当遇到有标记的节点时,就返回标记的值*区间长度,即该区间的总和。当搜寻到叶子节点,即l==r时,如果有标记,返回标记,没有则返回1(初始值为1,不曾改变)

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
using namespace std;
/******************************************************/
#define LL long long int
#define mem(a,b) memset(a,b,sizeof(a))
#define m ((l+r)/2)
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define L rt<<1
#define R rt<<1|1
#define N 100000+1
#define pow(a) a*a
#define INF 0x3f3f3f3f
#define max(a,b) (a>b?a:b)
#define min(a,b) (a<b?a:b)
/*********************************************************/
/*1
10
2
1 5 2
5 9 3*/
LL t, n, q;
LL addmark[N<<2];

void pushdown(LL rt){
    if (addmark[rt])
    {
            addmark[L] = addmark[rt];
            addmark[R] = addmark[rt];
        addmark[rt] = 0;
    }
}
void update(LL a, LL b, LL c, LL l, LL r, LL rt){
    if (a<=l&&r<=b){
        addmark[rt] = c;
        return;
    }
    pushdown(rt);
    if (a <= m)update(a, b, c, lson);
    if (b > m)update(a, b, c, rson);
}

LL add(LL l, LL r, LL rt){
    if (addmark[rt]){
        return addmark[rt] * (r - l + 1);
    }
    if (l == r){
        if (addmark[rt])
            return addmark[rt];
        else
            return 1;
    }
    LL res = 0;
    res+=add(lson);
    res+=add(rson);
    return res;
}
int main(){
    cin >> t;
    LL cnt = 1;
    while (t--){
        mem(addmark, 0);
        scanf("%lld%lld", &n, &q);
        while (q--){
            LL a, b, c;
            scanf("%lld%lld%lld", &a, &b, &c);
            update(a, b, c, 1, n, 1);
        }
        printf("Case %lld: The total value of the hook is %lld.\n",cnt++, add(1, n, 1));
    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值