码迷,mamicode.com
首页 > 其他好文 > 详细

POJ 3468 线段树(成段更新,区间求和)

时间:2015-09-10 22:23:32      阅读:223      评论:0      收藏:0      [点我收藏+]

标签:

题目链接:http://poj.org/problem?id=3468

题意:给定一个数列,每次操作可以是将某区间数字都加上一个相同的整数,也可以是询问一个区间中所有数字的和,对每次询问输出结果。

这个线段树运用了应用了add域优化,每个节点除了用value记录当前节点对应区间元素的和之外,还要用add域记录当前节点对应区间每个元素的增量。这样,没必要每次更新都要更新value更新到最底层每一个点,只需要将增量记录在某父节点的add域中即可,如果下次查询或者更新操作的是该父节点对应区间的子区间,那么就将add域更新下去,更新子节点的value值,完成更新或查询。add域优化可以减少时间复杂度。

还有就是要注意查询或者更新的区间游客能横跨左子树和右子树,在判断的时候应该做处理。

#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
#define maxn 4000000
struct node
{
    __int64 l;
    __int64 r;
    __int64 add;
    __int64 value;
    
}tree[maxn];
__int64 a[maxn],n;
void build( __int64 v, __int64 l,__int64 r )  //对节点v进行建立,区间为l到r
{
    tree[v].l=l;
    tree[v].r=r;
    if( l == r )
    {
        tree[v].value=a[r];   //完全二叉树
        return;
    }
    __int64 mid=( l+r )/2;
    build( v*2,l,mid );    //左儿子
    build( v*2+1,mid+1,r );   //右儿子
    tree[v].value=tree[v*2].value+tree[v*2+1].value;  //根据左右儿子更新当前节点
}
void update(__int64 v,__int64 l,__int64 r,__int64 m)  //更新区间l-r加上m
{
    tree[v].value+=m*(r-l+1);     //更新v点value
    if (tree[v].l==l && tree[v].r==r)    //找到了,更新并记录增量
    {
        tree[v].add+=m;
        return ;
    }
    
    if (tree[v].l==tree[v].r) return ;
    
    __int64 mid=(tree[v].l+tree[v].r)/2;
    
    if (tree[v].add)           //下边没更新,传递增量
    {
        tree[v*2].add+=tree[v].add;     //+=注意儿子本身可能就有原来未传递的增量
        tree[v*2+1].add+=tree[v].add;
        tree[v*2].value+=tree[v].add*(tree[v*2].r-tree[v*2].l+1);   //对value更新
        tree[v*2+1].value+=tree[v].add*(tree[v*2+1].r-tree[v*2+1].l+1);
        tree[v].add=0;         //传递完成
    }
    if (r<=mid)
    {
        update(v*2,l,r,m);   //只更新左儿子
        return;
    }
    if (l>mid)
    {
        update(v*2+1,l,r,m);  //只更新右儿子
        return;
    }
    
    update(v*2,l,mid,m);     //左右儿子都更新
    update(v*2+1,mid+1,r,m);
    
}
__int64 query( __int64 v,__int64 l,__int64 r)     //查询l-r上的v值
{
    if (l==tree[v].l&&r==tree[v].r)    //找到了
        return tree[v].value;   
    __int64 mid=(tree[v].l+tree[v].r)/2;
    
    if (tree[v].add)
    {
        tree[v*2].add+=tree[v].add;
        tree[v*2+1].add+=tree[v].add;
        tree[v*2].value+=tree[v].add*(tree[v*2].r-tree[v*2].l+1);
        tree[v*2+1].value+=tree[v].add*(tree[v*2+1].r-tree[v*2+1].l+1);
        tree[v].add=0;
    }
    
    if (r<=mid)
        return query(v*2,l,r);    //要查询的区间全在左儿子
    if (l>mid)
        return query(v*2+1,l,r);    //全在右边
    
    return query(v*2,l,mid)+query(v*2+1,mid+1,r);   //横跨左右边
}
int main()
{
    __int64 q,x,y,z,i;
    char h;
    
    scanf("%I64d%I64d",&n,&q);
    for (i=1;i<=n;i++)
        scanf("%I64d",&a[i]);
    
    build(1,1,n);
    for (i=1;i<=q;i++)
    {
        scanf("%s%I64d%I64d",&h,&x,&y);
        if (h==C) 
        {
            scanf("%I64d",&z);
            update(1,x,y,z);
        }
        else 
        {
            printf("%I64d\n",query(1,x,y));
        }
    }
    return 0;
}

暑假做的,又熟悉了一遍。。

POJ 3468 线段树(成段更新,区间求和)

标签:

原文地址:http://www.cnblogs.com/kiwibird/p/4799245.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!