zoukankan      html  css  js  c++  java
  • POJ 3468 线段树(成段更新,区间求和)

    题目链接: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
    ",query(1,x,y));
            }
        }
        return 0;
    }

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

  • 相关阅读:
    译:编程面试的10大算法概念汇总
    Android内存优化之封装九宫格
    Android Java 程序员必备开发工具
    译:如何成为一个通晓多种编程语言的程序员
    8大排序算法图文讲解
    Android酷炫实用的开源框架(UI框架)
    Android动态加载字节码
    利用无效字节码指令引发逆向工具崩溃(二)
    oracle 12c linux服务器启动监听
    oracle无主键去重方法
  • 原文地址:https://www.cnblogs.com/kiwibird/p/4799245.html
Copyright © 2011-2022 走看看