权值线段树,动态开合

一,情景导入

  • 维护在值域上的桶排序,在数据量不大的时候可以离线离散化
  • 也可以使用动态开点线段数缩小空间(缩至 2 ∗ n − 1 2*n-1 2n1),借而扩大桶的空间
    不过对于更擅长单点修改,获得区间和的树状数组优越性无法提现
  • 对(同一点)多种物品同时进行差分或者求和计数,考虑到每个点都开完整的统计桶数组太浪费,所以每个节点维护动态开点的权线(1-n 种物品的桶)
  • 条件:合并(子树)的时候搞定差分和前缀和的过程

二,动态开点并权线

i,概述

  • 1, 树上合并权线完成差分
  • 2, O ( 1 ) O(1) O(1) 寻找最值数目(差分值)
  • 3, 线段树上二分最值位置
  • 4,输出最值pos种类

ii, 合并的条件

所用同级同位线段树结构题非数据因素均相同(l,r)

iii,合并具体操作

泛化概述

  • 左右端点作为参数传递,不再存入
  • 结构体 l,r 代表是左右儿子编号,idx全局实时申请
  • r o t [ ] rot[] rot[] , 代表不同时刻线段树的根节点编号,方便索引
  • 所谓动态开点就是涉及哪个区间就开哪个部分,其余的保持空或者继承上个版本

1,核心函数

build, 申请新的节点
int build()
{
    ++tot;
    tr[tot].lc= tr[tot].rc= tr[tot].sum = 0;
    return tot;
}
insert (插入),实现动态开点和(新)信息储存
  • 递归终点为找到对应单点
  • 找到有交集区间开始递归
  • 传参包括 l,r
void insert(int &p,int l,int r,int val,int delta)
{
    if(!p) p =build();

    if(l==r)
    {
        tr[p].sum+= delta;
        return ;
    }

    int mid = l+r>>1;
    if(val<=mid) insert(tr[p].lc,l,mid,val,delta);
    else  insert(tr[p].rc,mid+1,r,val,delta);
    up(p);
}
merge (合并),实现回收节点和(新旧)信息合并
  • 新旧节点数据直接做和
  • 传参为待合并节点根编号
  • 维护最大值,准备线段树上二分最值
  • 合并顺序不定,注意这点不同与可持久数据结构
int merge(int p,int q,int l,int r)
{
    if(!p) return q;
    if(!q) return p;
    if(l==r)
    {
        tr[p].sum +=tr[q].sum;
        return p;
    }
    int mid = l+r >>1;
    tr[p].lc = merge(tr[p].lc,tr[q].lc,l,mid);
    tr[p].rc = merge(tr[p].rc,tr[q].rc,mid+1,r);
    up(p);
    return p;
}
query(线段树上二分) 维护最大值,类似于spaly
  • 返回了最值得序列位置(为离散化准备)
  • 传递最值pos参数亦可,实际复杂度差别不大
int query(int p,int l,int r)
{
    if(l==r)return l;
    int mid = l+r >>1;
    if(tr[tr[p].lc].sum>=tr[tr[p].rc].sum)return query(tr[p].lc,l,mid);
    else return query(tr[p].rc,mid+1,r);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

流苏贺风

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值