poj 3468 A Simple Problem with Integers 线段树 区间更新求和

线段树第二天,下午由于做了多校的第一场virtual judge,所以直到晚上才有时间来搞这道题

除了早上做的那个区间染色的问题,这个问题应该也是个入门题,当然由于鄙人智商是硬伤,没太反应明白,查了解题报告,再加上自己的一些尝试,最终侥幸AC了,下面说说思路:

首先,更新区间的时候不可能更新区间里的每一个点,这样复杂度太高

因此,在update的时候,找到对应的区间之后,直接给该区间存储一个变化值,我放在了add数组中,同时,对每个节点还维护了一个和,存在sum数组中,表示区间内所有数的和。push_up函数就是对父节点就子节点的和,push_down是重点,因为更新是只更新到区间的,那么当下一次更新是对这个已经更新过的区间的子区间进行更新时,必须将他的变化量传到子节点中去,举个例子,假设我第一次对(2,6)区间加2,第二次对(3,4)区间加1,此时,在第一次操作后,只有(2,6)对应的add有值,其子区间对应的add都是为0,所以,第二次去更新的时候,先把这个值传递到子区间中去,然后再向下找,这是为了保证整棵树维护的信息的正确性,而在父节点向子节点push_down的时候,可以对子节点的sum进行一下更新,以方便查找,具体的看push_down函数就可以体会。在对该节点的子节点插入后,得更新该节点的信息,即push_up一下

下面说查询,查询和update其实差不度,如果找到区间了,就直接返回区间的和,否则,还是得先push_down一下,再对子节点进行递归

以下是代码:

#include <cstdio>
#include <cstring>
const int MAX = 100010;
long long sum[MAX<<2];
long long add[MAX<<2];
void push_up(int rt)
{
    sum[rt] = sum[rt<<1]+sum[rt<<1|1];
}
void push_down(int l,int r,int rt)
{
    if(add[rt])
    {
        add[rt<<1] += add[rt];
        add[rt<<1|1] += add[rt];
        int mid = l+r>>1;
        sum[rt<<1] += (mid-l+1)*add[rt];
        sum[rt<<1|1] += (r-mid)*add[rt];
        add[rt] = 0;
    }
}
void build(int l,int r,int rt)
{
    if(l==r)
    {
        scanf("%lld",&sum[rt]);
        return;
    }
    int mid = r+l>>1;
    build(l,mid,rt<<1);
    build(mid+1,r,rt<<1|1);
    push_up(rt);
}
void update(long long p,int L,int R,int l,int r,int rt)
{
    if(L<=l&&R>=r)
    {
        add[rt]+=p;
        sum[rt]+=p*(r-l+1);
        return;
    }
    int mid = r+l>>1;
    push_down(l,r,rt);
    if(L<=mid) update(p,L,R,l,mid,rt<<1);
    if(R>mid) update(p,L,R,mid+1,r,rt<<1|1);
    push_up(rt);
}
long long query(int L,int R,int l,int r,int rt)
{
    if(L<=l&&R>=r)
    return sum[rt];
    int mid = r+l>>1;
    long long ans = 0;
    push_down(l,r,rt);
    if(L<=mid) ans+=query(L,R,l,mid,rt<<1);
    if(R>mid) ans+=query(L,R,mid+1,r,rt<<1|1);
    return ans;
}
int main()
{
    int n,k;
    while(scanf("%d%d",&n,&k)==2)
    {
        memset(add,0,sizeof(add));
        build(1,n,1);
        char c;
        int x,y;
        long long z;
        while(k--)
        {
            scanf(" %c",&c);
            if(c=='Q')
            {
                scanf("%d%d",&x,&y);
                printf("%lld\n",query(x,y,1,n,1));
            }
            else
            {
                scanf("%d%d%lld",&x,&y,&z);
                update(z,x,y,1,n,1);
            }
        }
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值