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\n",query(1,x,y));
        }
    }
    return 0;
}

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

时间: 2024-10-24 09:22:49

POJ 3468 线段树(成段更新,区间求和)的相关文章

poj 3468 线段树 成段增减 区间求和

题意:Q是询问区间和,C是在区间内每个节点加上一个值 Sample Input 10 51 2 3 4 5 6 7 8 9 10Q 4 4Q 1 10Q 2 4C 3 6 3Q 2 4Sample Output 455915 1 # include <iostream> 2 # include <cstdio> 3 # include <cstring> 4 # include <algorithm> 5 # include <cmath> 6

poj 3468 线段树成段更新

http://poj.org/problem?id=3468 A Simple Problem with Integers Time Limit: 5000MS   Memory Limit: 131072K Total Submissions: 58132   Accepted: 17704 Case Time Limit: 2000MS Description You have N integers, A1, A2, ... , AN. You need to deal with two k

(线段树成段更新+区间求和) poj 3468

D - A Simple Problem with Integers Time Limit:5000MS     Memory Limit:131072KB     64bit IO Format:%I64d & %I64u Submit Status Practice POJ 3468 Description You have N integers, A1, A2, ... , AN. You need to deal with two kinds of operations. One typ

poj 3669 线段树成段更新+区间合并

添加 lsum[ ] , rsum[ ] , msum[ ] 来记录从左到右的区间,从右到左的区间和最大的区间: #include<stdio.h> #define lson l,m,rt<<1 #define rson m+1,r,rt<<1|1 #define maxn 50005 int rsum[maxn<<2],lsum[maxn<<2],msum[maxn<<2];//msum[]维护区间1…N中的最大连续区间长度 int

POJ训练计划2777_Count Color(线段树/成段更新/区间染色)

解题报告 题意: 对线段染色,询问线段区间的颜色种数. 思路: 本来直接在线段树上染色,lz标记颜色.每次查询的话访问线段树,求出颜色种数.结果超时了,最坏的情况下,染色可以染到叶子节点. 换成存下区间的颜色种数,这样每次查询就不用找到叶子节点了,用按位或来处理颜色种数. #include <iostream> #include <cstdio> #include <cstring> #include <algorithm> using namespace

HDU 3397 Sequence operation(线段树&#183;成段更新&#183;区间合并&#183;混合操作)

题意  给你一个只有0, 1的数组  有这些操作 0. 将[a, b]区间的所有数都改为0 1. 将[a, b]区间的所有数都改为1 2. 将[a, b]区间的所有数都取反 即与1异或 3. 输出区间[a, b]中1的个数  即所有数的和 4. 输出区间[a, b]中最大连续1的长度 对于所有的3, 4操作输出对应的答案 单个的操作都很简单  但搞在一起就有点恶心了  还好数组里的数只有0和1 线段树维护9个值 对应区间0, 1的最大长度len[i]  对应区间左端点为起点的最大0, 1长度ll

poj 2777 Count Color (成段更新+区间求和)

Count Color Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 36646   Accepted: 11053 Description Chosen Problem Solving and Program design as an optional course, you are required to solve all kinds of problems. Here, we get a new problem.

NYOJ 1068 ST(线段树之 成段更新+区间求和)

ST 时间限制:1000 ms  |  内存限制:65535 KB 难度:1 描述 "麻雀"lengdan用随机数生成了后台数据,但是笨笨的他被妹纸的问题给难住了... 已知lengdan生成了N(1=<N<=10005)个随机整数,妹子对这些数可能有以下几种操作或询问: 1,A a b c 表示给区间a到b内每个数都加上c: 2,S a b  表示输出区间a到b内的和: 3,Q a b 表示区间a到b内的奇数的个数: 为了使妹纸不口渴,所以我们决定妹纸的询问次数少一点,即

poj 3468 A Simple Problem with Integers (线段树成段更新)

A Simple Problem with Integers Time Limit: 5000MS   Memory Limit: 131072K Total Submissions: 77486   Accepted: 23862 Case Time Limit: 2000MS Description You have N integers, A1, A2, ... , AN. You need to deal with two kinds of operations. One type of

poj 3468 A Simple Problem with Integers 【线段树-成段更新】

题目:poj 3468 A Simple Problem with Integers 题意:给出n个数,两种操作 1:l -- r 上的所有值加一个值val 2:求l---r 区间上的和 分析:线段树成段更新,成段求和 树中的每个点设两个变量sum 和 num ,分别保存区间 l--r 的和 和l---r 每个值要加的值 对于更新操作:对于要更新到的区间上面的区间,直接进行操作 加上 (r - l +1)* val .下面的区间标记num += val 对于求和操作,每次进行延迟更新,把num值