线段树更进一步的运用

区间修改
线段树除了点修改之外还有更多的作用
Add(l,r,v):把a[l],a[l+1],a[l+2]…a[r]中所有元素加v
Query(l,r):计算子序列a[l],a[l+1],…,a[r]的元素和,最小值和最大值

当然如果你想让你的代码拥有更多的黑科技气息(附带运算速度加成),你可以采用 位运算 这种优(zhuang)秀(bi)的技巧

定义线段树需要维护的区间和,最小值和最大值分别为sum,min,max.
信息维护的代码如下,假设修改/查询范围均为[y1,y2]


   void maintain(int o,int l,int r){
    int lc=o*2,rc=o*2+1;
    sum[o]=minv[o]=maxv[o]=0;  //这一语句不能删,它是用来淸零叶节点的
    if(r>l){  // 维护左右子树 
        sum[o]=sum[lc]+sum[rc];
        minv[o]=min(minv[lc],minv[rc]);
        maxv[o]=max(maxv[lc],maxx[rc]);
    }
    minv[o]+=addv[o];  maxv[o]+=addv[o]; 
    sum[o]+=addv[o]*(r-l+1));
}

下面是执行add操作时,将访问到的结点进行信息维护

void update(int o,int l,int r){
    int lc=o*2,rc=o*2+1;
    if(y1<=l && y2>=r)  addv[o]+=v;
    else {
        int m=l+(r-l)/2;
        if(y1<=m)  update(lc,l,m);
        if(y2>m)   update(rc,m+1,r);
    }
    maintain(o,l,r);
}

查询操作就是将查询操作递归分解为若干个不相交子区间,把各个子区间的查询结果加以合并.

void query(int o,int l,int r,int add){ //add表示当前区间的所有祖先结点add值的和 
    if(y1<=l && y2>=r) {
        sumv+=sum[o]+add*(r-l+1);
        min=minx(min,minv[o]+add);
        max=maxx(max,maxx[o]+add);
    }
    else {
        int m=l+(r-l)/2;
        if(y1<=m)  query(o*2,l,m,add+addv[o]);
        if(y2>m)   query(o*2+1,m+1,r,add+addv[o]);
    }
}

线段树还有更多巧妙的运用,例如:

Set(l,r,v): 把a[l],a[l+1],…,a[r]的值全部修改为v
Query(l,r,v): 计算字段和a[l],a[l+1],…,a[r]的元素和,最小值和最大值

set操作也可以类似于add操作进行分解,但是add操作先后顺序可以交换,而set操作却有着严格的先后顺序.比如执行add(1,4,1)在执行add(2,3,2)等价于add(2,3,2)后再执行add(1,4,1),但set不一样.所以set还要修改以前分解好的.

这样以下为修改代码

void pushdown(int o){ //pushdown函数作用是将set值往下传递 
    int lc=o*2,rc=o*2+1;
    if(setv[o]>=0) {
        setv[lc]=set[rc]=setv[o];
        setv[o]=-1;
    } 
}
void update(int o,int ql,int qr){
    int lc=o*2,rc=o*2+1;
    if(y1<=l && r>=y2)  setv[o]=v;
    else {
        pushdown(o);
        int m=l+(r-l)/2;
        if(y1<=m) update(lc,ql,m);
        if(y2>m)  update(rc,m+1,qr);
    }
    maintain(o,l,r);
}

以上就是线段树的一些区间的简单操作(贼爽,速度快得一比).
但线段树复(zhuang)杂(bi)的操作远远不止这些.

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值