POJ3468(区间更新线段树)

 因为long long类型数据输入的错误搞了大半天,所以采用scanf和printf输入输出一定严格遵守格式控制。

先推荐一下大佬的博客链接:https://blog.csdn.net/dt2131/article/details/58689903

先说一下自己的错误吧~:输出的时候要严格按照格式,输入的时候也相同,比如,在这道题目的程序中scanf("%lld",&tree[rt])

是正确的,但是我一开始的错误输入scanf("%d",&tree[rt])在处理负数的时候会出现错误的输出,因此使用标准输入输出一定要严格遵守格式限制的规定,占位符非常重要!!!

当然,除此之外还要注意:返回的函数值一定要符合函数值的返回类型,否则也会出错。

下面是题目的AC代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=100005;
char str[5];
long long add[maxn<<2];//增加下推标记
long long tree[maxn<<2];
int L,R,C;
int n,m;
void pushup(int rt)
{
    tree[rt]=tree[rt<<1]+tree[rt<<1|1];
    //printf("%d数字已经更新\n",tree[rt]);
    return ;
}
void pushdown(int ln,int rn,int rt)
{//ln,rn分别是左子树和右子树的数字数量
    if(add[rt])
    {//首先将标记下推
        add[rt<<1]+=add[rt];
        add[rt<<1|1]+=add[rt];
     //其次将左子树和右子树的结点向下更新
        tree[rt<<1]+=add[rt]*ln;
        tree[rt<<1|1]+=add[rt]*rn;
        add[rt]=0;
    }
}
void build_tree(int l,int r,int rt)
{
    if(l==r)
    {
        scanf("%lld",&tree[rt]);
        //printf("%d数字已经录入\n",tree[rt]);//检验
        return;
    }
    int m=(l+r)>>1;
    build_tree(l,m,rt<<1);
    build_tree(m+1,r,rt<<1|1);
    pushup(rt);
}
void update(int l,int r,int rt)
{
    if(L<=l&&r<=R)
    {
        tree[rt]+=(r-l+1)*C;
        add[rt]+=C;//增加懒惰标记,表示本区间的tree值
        //正确,不再下推,子区间的值使用的时候再进行修改
        return;
    }
    int m=(l+r)>>1;
    pushdown(m-l+1,r-m,rt);
    if(L<=m)
        update(l,m,rt<<1);
    if(m<R)
        update(m+1,r,rt<<1|1);
    pushup(rt);
}
long long int query(int l,int r,int rt)
{
    if(L<=l&&r<=R)
    {
       ///printf("%d已经被查询到\n",tree[rt]);
       return tree[rt];
    }
    int m=(l+r)>>1;
    pushdown(m-l+1,r-m,rt);
    long long ans=0;
    if(L<=m)
        ans+=query(l,m,rt<<1);
    if(m<R)
        ans+=query(m+1,r,rt<<1|1);
        ///printf("返回的查询值为%d\n",ans);
    return ans;
}
int main()
{
    memset(add,0,sizeof(add));
    memset(tree,0,sizeof(tree));
    scanf("%d%d",&n,&m);
    build_tree(1,n,1);
    for(int i=0;i<m;++i)
    {
        scanf("%s",str);
        if(str[0]=='Q')
        {//区间查询
            scanf("%d%d",&L,&R);
            printf("%lld\n",query(1,n,1));
        }
        else if(str[0]=='C')
        {//区间修改
            scanf("%d%d%d",&L,&R,&C);
            update(1,n,1);
        }
    }
    return 0;
}

新的知识点需要理解的是线段树的下推函数和懒惰标记的使用。

懒惰标记的作用有两个,一个是记录遍历到的位置,另一个是记录下推时要更新多大的值。

通过代码可以看出:在下一次进行区间更新或者区间查询的时候,在当前区间没有落到目标区间时,我们总要首先对当前区间进行下推操作,目的是更新子区间的值,使子区间的值保持正确后,再进行下面的利用。而一旦将懒惰标记使用后(下推操作完成,改变左子树数值和右子树数值,并且将懒惰标记的位置转移到左子树和右子树上后),懒惰标记的两个作用:遍历到的位置下移了,因此不能再使用这个懒惰标记记录当前位置作为遍历到的位置了,记录更新多大的值也转移到左子树和右子树的懒惰标记上了,因此这个懒惰标记失去了它的作用和存在的意义,于是需要add[rt]=0操作。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值