权值线段树 学习笔记

权值线段树 学习笔记

权值线段树可以用来代替平衡树的一些操作,如果不知道平衡树是干什么用的,请先学习平衡树。

首先,我们想一下线段树可以干什么??

区间加,单点加,区间和……其实还有很多操作,只不过权值线段树一般只用到这几个比较常见的操作。

权值线段树的实质是对值域建线段树,权值线段树对值域中的每一个值都建了一个叶子节点,所以建树时间复杂度是 \(\Theta (n\log_2 [l,r])\)\([l,r]\) 是值域。单点修改,插入就是 \(\log_2[l,r]\)

考虑平衡树的添加一个数,因为值域中的每一个值都对应一个叶子,所以我们直接找到这个叶子节点,将它的数量加 \(1\) 就可以了,删除一个数同理。

求第 \(k\) 大和平衡树差不多,如果 \(k\) 小于等于左儿子就递归左子树,大于就递归右儿子,同时 \(k\) 减去左儿子的大小。

\(x\) 为第 \(k\) 大也和平衡树差不多,如果 \(x\le mid\) 就递归左子树,如果 \(x>mid\) 就递归右子树,同时 \(k\) 加右子树的大小。

因为值域一般比较大,而空间复杂度是 \(\Theta([l,r])\) ,很多题目会炸掉,所以一般采用两种方法来优化。

第一种方法是离散化,将所有操作的数据离散化为 \([1,n]\) 然后在 \([1,n]\) 建树,每次操作实际修改离散化后的值,只适合可以离线的题目,但是这种方法无论是时间复杂度还是空间复杂度都优于第二种,而且写法简单。建树 \(\Theta (n\log_2n)\)\(n\) 是数据范围,单次操作时间复杂度为 \(\Theta (\log_2n)\) ,空间复杂度为 \(\Theta (n)\)

第二种方法是动态开点线段树,这种方法虽然在很多方面不如第一种,但是可以在线。考虑在值域中二分每次要操作的值,只需要 \(\log_2[l,r]\) 的时间复杂度,所以我们考虑每次把二分用到的点在线段树上现用现开,这样就可以实现在线操作。所以建树时间复杂度为 \(\Theta (n\log_2[l,r])\) ,单次操作为 \(\Theta (\log_2 [l,r])\) ,空间复杂度为 \(\Theta (n\log_2[l,r])\)

但是需要注意的是权值线段树只能维护正数,不能维护负数,所以在处理负数的时候要统一加一个基数。

几道例题。

\(luogu\ P1168\)中位数

直接离线权值线段树,然后不断加数在 \(i\) 为奇数的时候维护第 \(i/2+1\) 大就可以了。

代码

\(luogu\ P2234 [HNOI2002]\)营业额统计

离线或者在线每次差插入的数是第 \(k\) 大,然后再查 \(k-1\) 小或者 \(k+1\) 小在减一下,取个 \(\min\) 就可以了。

代码

\(luogu\ P1486 [NOI2004]\)郁闷的出纳员

维护一个全局 \(tag\) 在每次全局加减的时候对 \(tag\) 进行操作,然后在删除的时候对 \(min\) 进行一个 \(tag\) 的逆运算,把小于等于逆运算后的数区间删除,因为加入权值线段树的值是不变的,但是 \(tag\) 是不断变化的,所以我们对当前的操作的数进行一个 \(tag\) 的逆运算,就是权值线段树中要操作的数字。插入也要进行一个 \(tag\) 的,然后每次直接查第 \(k\) 大就可以了。

需要注意的是因为进行 \(tag\) 逆运算可能有负数,所有这道题目需要统计加一个基数,然后输出的时候再减掉基数。

代码

转载于:https://www.cnblogs.com/last-diary/p/11544625.html

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值