简要复习线段树

线段树(Segment Tree)的组成

主要针对题目

以下出现的k为线段树内代表区间的点的编号


数据

  • 点的个数 n
  • 该点代表的区间各数之和 sum[4*maxn]
  • 该点代表的区间上改变的量 add[4*maxn]
sum[k] 和 add[k] 的 区分

比如说 , k代表的区间上每个数都加上num,
则变化后的sum[k] = sum[k] + (区间长度) * num
且变化后的add[k] = add[k] + num

函数


建树
  • 目标:得到所有区间的sum[k]
  • 过程:分治递归区间。

    递归终止:区间内只含有一个数,即 l == r 时 ,sum[k] = res[l] 或 res[r]

    子区间递归结束后,

    递归过程 sum[k] = sum[k << 1] + sum[k << 1 | 1]

  • 注意

    递归子区间时,两个子区间的k应分别 << 1 或 << 1 | 1(即 << 1后 + 1),

    以保证对于每个区间,编号k是唯一的,且可从比它们更大的区间到达


更新区间点值
  • 目标:更新涉及到的区间的sum[k] , 并更新目标区间的add[k]
  • 过程:分治递归区间。

    递归终止:当前区间等价于目标区间时 , 更新其add[k](即 add[k] = add[k] + 要加到目标区间的值)

    递归过程:

    1. 当前区间的sum[k]都必须更新(即sum[k] = sum[k] + 目标值 * 目标区间长度)

    2. 目标区间必须为当前区间的子集,

      如果递归到下一层时 当前区间中点在目标区间内 而 当前区间的两端在目标区间外 , 则应将目标区间与当前区间同时分治 ;

      如果递归到下一层时 当前区间中点在目标区间外 ,

      若目标区间完全为 当前区间左端点 到 当前区间中点 之间区间 的子集 , 则只在这个新区间更新 ;

      若目标区间完全为 当前区间中点 到 当前区间右端点 之间区间 的子集 , 则只在这个新区间更新 。


查询区间和
  • 目标 :获得目标区间的sum[k]和 包含目标区间的父区间的add[k] 与 目标区间长度 的 乘积 的 加和
  • 过程:依旧是分治递归区间。

    递归终止:当前区间等价于目标区间时 ,返回其sum[k] 及 一路下来父区间的add[k']之和 与 目标区间长度 的 乘积 的 加和

    递归过程 :

  1. 将当前区间的add[k]加入下一层查询的参数中

  2. 与 “更新区间点值 -> 过程 -> 递归过程 2.” 大同小异


其他
  • 快读快写之类的常数优化不再赘述

线段树(Segment Tree)的代码


C++98/03
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <iostream>
typedef long long ll;
ll read();
const ll MAXN = ll(4e6) + 40;
ll n,m,sum[MAXN],add[MAXN],res[MAXN / 4];
void build(ll,ll,ll);//建树
void update(ll,ll,ll,ll,ll,ll);//更新区间点值
ll   query_sum(ll,ll,ll,ll,ll,ll);//查询区间和
int main()
{
    n = read(); m = read();
    for(ll t = 1 ; t <= n ; ++t ){
        res[t] = read();
    }
    build(1,1,n);
    for(ll t = 1 ; t <= m ; ++t ){
        ll opCode,x,y,k;
        opCode = read();
        x = read(); y = read();
        if(opCode == 1){
            k = read();
            update(1,1,n,x,y,k);
        }else if (opCode == 2){
            printf("%lld\n",query_sum(1,1,n,x,y,0));
        }
    }
    return 0;
}
ll query_sum(ll k , ll l , ll r , ll x , ll y , ll v ){
    if(x == l && y == r){
        return sum[k] + (r - l + 1) * v;
    }
    ll mid = ( l + r ) >> 1 ;
    if(y <= mid)
        return query_sum(k << 1 , l , mid , x , y , v + add[k] );
    else if(x >= mid + 1 )
        return query_sum(k << 1 | 1 , mid + 1 , r , x , y ,v + add[k]);
    return 
        query_sum(k << 1 , l ,mid , x , mid , v  + add[k])  
        +
        query_sum(k << 1 | 1 , mid + 1 , r ,mid + 1 , y , v + add[k]);
}
void update(ll k, ll l , ll r , ll x , ll y , ll v ){
    sum[k] += v * (y - x + 1);
    if(l == x && r == y){
        add[k] += v; 
        return;
    }
    ll mid = (l + r) >> 1;
    if(y <= mid)
        update(k << 1 , l , mid , x , y , v );
    else{
        if(x > mid)
            update(k << 1 | 1 , mid + 1 , r , x , y , v);
        else
            update(k << 1 , l , mid , x , mid , v),
            update(k << 1 | 1 , mid + 1 , r , mid + 1 , y , v );
    }
}
void build(ll k , ll l ,ll r){
    if(l == r){
        sum[k] = res[(l+r) >> 1];
        return;
    }
    ll mid = (l + r) >> 1;
    build(k << 1 , l , mid);
    build(k << 1 | 1 , mid + 1, r);
    sum[k] = sum[k << 1] + sum[k << 1 | 1];
}
ll read()
{
    ll ans = 0 , mark = 1; char c = getchar();
    while(c < '0' || c > '9'){
        if(c == '-')
            mark = -1;
        c = getchar();
    }
    while(c >= '0' && c <= '9'){
        ans = ans * 10 + c -'0';
        c = getchar();
    }
    return ans * mark;
}

转载于:https://www.cnblogs.com/StarOnTheWay/p/11259398.html

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值