线段树(写法剖析和一些细节)

线段树(写法剖析和一些细节)

#include<bits/stdc++.h>
#define lson root<<1
#define rson root<<1|1
typedef long long LL ; 
using namespace std;
const int MAXN = 1e5+1 ;
struct SgT{
    int l, r ;
    LL add ; // the plus lazy tag
    LL sum ;
}T[MAXN<<2] ;

int n, m ;

void Merge(int root){
    T[root].sum = T[lson].sum + T[rson].sum ;
}

void TagPushDown(int root){
    if(T[root].add == 0) return ;
    // to cutdown the const ......
    int len = T[root].r - T[root].l + 1 ;
    T[lson].sum += (len - (len>>1) ) * T[root].add ;
    // the lson is bigger sometimes
    T[rson].sum += (len>>1) * T[root].add ;
    T[lson].add += T[root].add ; 
    T[rson].add += T[root].add ;
    T[root].add = 0 ;
}

void Build(int l, int r, int root){
    T[root].l = l ;
    T[root].r = r ;
    if(l == r){
        scanf("%lld", &T[root].sum) ;
        return ; 
    }
    int mid = (l+r)>>1 ;
    Build(l, mid, lson) ; // left-son-tree
    Build(mid+1, r, rson) ; //right-son-tree
    Merge(root) ;
}

void Update(int L, int R, int root, LL key){
    // you want to update [L,R]
    // totally included
    if(T[root].l >= L && T[root].r <= R){
        T[root].add += key ;
        T[root].sum += (T[root].r - T[root].l + 1) * key ;
        return ; 
    }
    // not included in [L,R]
    // put down the lazy tag 
    TagPushDown(root) ;   

    // the initial recursion part

    // if(T[lson].r >= L && T[lson].l <= R) Update(L, R, lson, key) ;
    // if(T[rson].l <= R && T[rson].r >= L) Update(L, R, rson, key) ;
    
    // after thinking about it, I choose the following code

    int mid = (T[root].l + T[root].r)>>1 ;
    if(mid >= L) Update(L, R, lson, key) ;
    if(mid < R) Update(L, R ,rson, key) ;
    // take the lson for example 
    // we don't need the T[lson].r <= R
    // because the second line promises the situation doesn't exist
    Merge(root) ;
}

LL Query(int L, int R, int root){
    // you want to get the sum 
    if(T[root].l >= L && T[root].r <= R){
        return T[root].sum ; 
    }
    // no matter update or query
    // if you visit the smaller region
    // you have to put down your lazy tag
    TagPushDown(root) ;
    LL re=0 ; 
    int mid = (T[root].l + T[root].r)>>1 ;
    if(mid >= L) re += Query(L, R, lson) ;
    if(mid < R) re += Query(L, R, rson) ;
    // no update, no merge
    return re ;
}

int main(){
    freopen("in.txt", "r", stdin) ;
    scanf("%d%d", &n, &m) ;
    Build(1, n, 1) ;
    int x, y, flag ;
    LL k ;
    for(int i=1 ; i<=m ; i++ ){
        scanf("%d%d%d", &flag, &x, &y) ;
        if(flag == 1){
            scanf("%lld", &k) ;
            Update(x, y, 1, k) ;
        }
        else {
            printf("%lld\n", Query(x, y, 1) ) ;
        }
    }
    return 0 ;
}

不对算法思想进行解说,只分析一下代码实现过程中的细节与思考。

void Update(int L, int R, int root, LL key){
    if(T[root].l >= L && T[root].r <= R){
        T[root].add += key ;
        T[root].sum += (T[root].r - T[root].l + 1) * key ;
        return ; 
    }
    TagPushDown(root) ;   
    int mid = (T[root].l + T[root].r)>>1 ;
    if(mid >= L) Update(L, R, lson, key) ;
    if(mid < R) Update(L, R ,rson, key) ;
    Merge(root) ;
}

如果当前的区间被完全包含在了访问的区间中,我们就打上lazy标记,同时不在往下递归。如果没有被完全包含,那么我们的标记要传下去。无论访问是否包含两个子区间,因为有不包含整个大区间的修改,当前的大区间已经能包含两个子区间的信息。

关于递归部分,代码经过了一些修改。
一开始是写成这样的:

	if(T[lson].r >= L && T[lson].l <= R) Update(L, R, lson, key) ;
	if(T[rson].l <= R && T[rson].r >= L) Update(L, R, rson, key) ;

这样写起来感觉挺符合思路的。但是分析之后,发现有冗余的判断。
观察左子区间,会不会出现左区间的左端点,就是本区间的左端点,还在查询区间的右边的情况?

如果出现这样的情况,说明在上一级的区间中,本区间作为右子区间进入了递归。但是观察到第二条语句的条件,这样的情况不可能发生。所以这条判断可以去掉。同理,右子区间中的判断也可以删掉。

改成了这个样子:

	if(T[lson].r >= L) Update(L, R, lson, key) ;
	if(T[rson].l <= R) Update(L, R, rson, key) ;

感觉可能常数会大,又改成了这个样子:

    int mid = (T[root].l + T[root].r)>>1 ;
    if(mid >= L) Update(L, R, lson, key) ;
    if(mid < R) Update(L, R ,rson, key) ;

标记传递是写成这个样子的:

void TagPushDown(int root){
    if(T[root].add == 0) return ;
    // to cutdown the const ......
    int len = T[root].r - T[root].l + 1 ;
    T[lson].sum += (len - (len>>1) ) * T[root].add ;
    // the lson is bigger sometimes
    T[rson].sum += (len>>1) * T[root].add ;
    T[lson].add += T[root].add ; 
    T[rson].add += T[root].add ;
    T[root].add = 0 ;
}

左子区间长度总是不小于右子区间的(有时相等,有时大1),所以长度可以写成(len - (len>>1) )。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值