动态开点

文章介绍了权值线段树在处理大范围区间时,由于常规建树方法会导致空间超限,因此提出了动态开点的优化策略。在需要插入新区间时才创建节点,减少空间使用。插入操作根据值的位置决定搜索左子树或右子树,并更新节点状态。这种方法将空间复杂度降低到线性级别。
摘要由CSDN通过智能技术生成

引入

我们在一般的线段树中,代码都是如下的:

void build(int x, int l, int r) {
    if (l == r) {
        tree[x].size = 1;
        return ;
    }
    int mid = (l + r) / 2;
    build(x * 2, l, mid);
    build(x * 2 + 1, mid + 1, r);
    updata(x);
}

这在普通的线段树当然可以。

但是权值线段树的区间往往大于 1 0 9 10^9 109 ,此时我们按照这样的思路来做就会发现空间会超限。

这时候我们就需要用到动态开点了。

思路

我们不会有建树操作,我们只会有插入操作。

当我们枚举到一个区间时,却发现这个区间之前没有枚举过,也就是这个区间没被记录过,我们就开一个节点来记录。

if (x == 0)
    x = ++cnt;

因为我们不再是按照一定的规律给点编号,我们就需要记录一下这个节点的左右儿子。(我用的时结构体)

struct node {
    int lson, rson, size;
} tree[10000005];

然后我们在分区间的时候不是两个区间都去枚举,而是我们要插入的数在那个区间就枚举那个。

int mid = (l + r) / 2;
    if (val <= mid)
        insert(tree[x].lson, l, mid, val);
    else
        insert(tree[x].rson, mid + 1, r, val);

我们来看看因为他每次最多开 l o g 2 ( 值域 ) log_2(值域) log2(值域) 个节点,所以他一共就会开 n l o g 2 ( 值域 ) nlog_2(值域) nlog2(值域) 个节点,这样子我们就可以存的下了。

代码

struct node {
    int lson, rson, size;
} tree[10000005];
void insert(int &x, int l, int r, int val) {
    if (x == 0)//如果当我枚举到这个点时,我却没有记录,那我们就开一个节点
        x = ++cnt;
    tree[x].size++;
    if (l == r)
        return ;
    int mid = (l + r) / 2;
    if (val <= mid)//如果我要插入的值在左边,就搜左边,要不就搜右边
        insert(tree[x].lson, l, mid, val);
    else
        insert(tree[x].rson, mid + 1, r, val);
    updata(x);//更新这个点的状态
}
  • 8
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值