POJ 3468 A Simple Problem with Integers (线段树—成段覆盖)

6 篇文章 0 订阅
1 篇文章 0 订阅

题意:给出[1,n]区间内每个点的数值,让你执行下面的操作:
      1. C a b w : 区间[a,b]上所有点的数值加上w。
      2. Q a b : 输出区间[a,b]上所有点的数值之和。

思路:经典线段树。静态建树,成段修改,区间求和。用普通的线段树去做肯定超时,因为成段修改的时候会是o(n)。关键在于用add记录对应区间内所有元素的增量,并对查询函数进行相应的修改。注意修改和查询的一个很关键的性质:区间[node[u].left,node[u].right]必定包含区间[left,right]。


//7088K    2219MS
#include <stdio.h>
#include <string.h>
#define N 100050
#define MAX(X,Y) ( (X) > (Y) ? (X) : (Y) )
#define MIN(X,Y) ( (X) < (Y) ? (X) : (Y) )
#define L(X) ( (X) <<1 )
#define R(X) ( ( (X) << 1 )| 1 )
#define MID(X) ( (X)>>1 )
struct node
{
    int left , right ;
    double sum , add ;      //  sum存储区间数值之和,add存储区间内所有数的增量。
}node[4*N];

double num[N] ;

double ans ;

void BuildTree ( int const left , const int right , int n )
{
    node[n].left = left ;
    node[n].right = right ;
    node[n].add = 0 ;
    if ( left == right )
    {
        node[n].sum = num[left] ;
    }
    else
    {
        int mid ;
        mid = MID ( left + right ) ;
        BuildTree ( left , mid , L(n)) ;
        BuildTree ( mid + 1 , right , R(n) ) ;
        node[n].sum = node[L(n)].sum + node[R(n)].sum ;
    }
}

void Update ( int const left , int const right , double const val , int n )
{
    if ( node[n].left == left && node[n].right == right )
    {
        node[n].add += val ;    //  情况1:两区间完全匹配,新增的值记录为区间的add。
        return ;
    }
    node[n].sum += ( right - left + 1 ) * val ;      //  情况2:区间要继续分割,大区间的sum加上小区间所有数值新增的总和。
    if ( left <= node[L(n)].right )     //  区间分割要考虑全面。
    {
        int temp_r ;
        temp_r = MIN( node[L(n)].right , right ) ;
        Update ( left , temp_r , val , L(n) ) ;
    }
    if ( right >= node[R(n)].left )     //  区间分割要考虑全面。
    {
        int temp_l ;
        temp_l = MAX( left , node[R(n)].left ) ;
        Update ( temp_l , right , val , R(n) ) ;
    }
}

void Query ( int const left , int const right , int const n )
{
    ans += ( right - left + 1 ) * node[n].add ;      //  先加上区间[left,right]记录在[node[i].l,node[i].r]的总增量。
    if ( node[n].left == left && node[n].right == right )       //  情况1:两区间完全匹配。
    {
        ans += node[n].sum ;
    }
    else if ( right <= node[L(n)].right )        //  情况2:小区间被大区间的左子区间包含。
    {
        Query ( left , right , L(n) ) ;
    }
    else if ( left >= node[R(n)].left)           //  情况3:小区间被大区间的右子区间包含。
    {
        Query ( left , right , R(n) ) ;         //  情况4:小区间被大区间的两个子区间分割。
    }
    else
    {
        int mid ;
        mid = MID ( node[n].left + node[n].right ) ;
        Query ( left , mid , L(n) ) ;
        Query ( mid + 1 , right , R(n) ) ;
    }
}

int
main ( )
{
    int n , m ;
    scanf ("%d%d" , & n , & m ) ;
    int i ;
    for ( i = 1 ; i <=n ; i ++ )
    {
        scanf ("%lf" , & num[i] ) ;
    }
    getchar ( ) ;
    BuildTree ( 1 , n , 1 ) ;
    while ( m -- )
    {
        char oper ;
        int left , right ;
        scanf ("%c" , & oper ) ;
        if ( oper == 'Q' )
        {
            scanf ("%d%d" , & left , & right ) ;
            ans = 0 ;
            Query ( left , right , 1 ) ;
            printf ("%.0f\n" , ans ) ;
        }
        else
        {
            double val ;
            scanf ("%d%d%lf" , & left , & right , & val ) ;
            Update ( left , right , val , 1 ) ;
        }
        getchar( ) ;
    }
    return 0 ;
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值