线段树题单

Problem 1

P4588 [TJOI2018]数学计算(没区间没数列没树但是要用线段树...)


维护线段树,支持区间求积,单点修改即可。水题不讲。

Code

Problem 2

P4145 上帝造题的七分钟 2 / 花神游历各国


发现一个<1012的数开6次根一定是1,然后无论开多少次根都不变了。可以维护一个最大值的线段树,如果区间内全是1就不管,否则暴力遍历开方。(在最大值线段树上找非1数是不行的,最坏nlog2n

然后一个个直接更新不需要lazytag,最坏O(nlogn),但是完全是O(nlogn)的操作最多出现6次,可以通过。

水蓝水蓝

Code

Problem 3

P2087 GTY的人类基因组计划2

请不要使用n2logn的暴力,这会让这道下位紫题变成思维绿。


Part 1

很显然,我们需要快速地查询两个组合是否相等,还需要自由地在组合间移动人。

我们可以使用hash. 给每个人一个随机生成的tag,而组合的hash值就是所有成员的tag异或起来。增加/减少成员时,把组合的hash值异或上移动了的成员的tag即可。

我们把已经做过实验的组合的hash扔到set里,查询有没有做过实验时直接s.find(hash)==s.end()?true:false

Part 2

做一个资瓷单点修改,区间ban(被ban的房间人数变为0),单点复原,区间求和的线段树。

只有一个bool型的lazytag,表示当前区间有没有被全ban.

区间求和计算方法和普通的不一样,即使当前区间完全被查询区间覆盖,如果没有全ban,也要往深层继续查询。

单点修改正常修改,但是修改以后被ban的情况可能会发生改变,新写一个update函数,一路从根节点pushdown下去,最后改一下单点(叶子结点)的lazytag即可。

被ban的区间内的房间的hash并不需要立刻存入set内,在pushdown到叶子结点的时候存就可以了。原因自己想。(hint:所有成员都是唯一的)

Part 3

一些废话:

  1. 随机函数最好自己写,不难。

  2. 成员换房间的时候先更新人数,再update两个房间的lazytag(这时先不改那两个房间的实际hash值),然后最后再改hash值。原因自己想。(hint:pushdown会存hash 这已经不是hint而是最终答案了(

  3. 成员可能会“从原来的房间到原来的房间”,就是完全没动。这种情况一定要特判然后忽略,不然hash会出大问题。

Code

Problem 4

P2572 [SCOI2010] 序列操作(线段树操作大杂烩了属于是)

题目相当于要维护一个01线段树,支持区间推平,区间取反,区间求和,区间最长连续1.

我们一个一个来看。

维护数据

区间推平

维护两个lazytag,ass0ass1,分别表示把当前区间推平成0/1

区间取反

再维护一个lazytag rev,表示取反当前区间。

区间求和

维护一个数据sum1,表示当前区间1的个数(等价于区间和)。

区间最长连续1

GSS3相似,维护三个东西:区间左边最长连续1,区间右边最长连续1,区间最长连续1. 分别用left1,right1,cont1表示。

Extra

在实际情况中,还要维护区间左边最长连续0,区间右边最长连续0,区间最长连续0. 分别用left0,right0,cont0表示。具体作用后面会说。


所以我们的线段树有以下的东西:

结构:l,r,lc,rc.(区间和子树,线段树必需)

lazytag:rev,ass0,ass1.

数据:sum1,left0,right0,left1,right1,cont0,cont1.

数据间影响

lazytag之间

lazytag之间的优先级不像线段树2一样,比较好维护,所以可以让每一个节点保证同时只有一个lazytag。

把“从祖先push_down下来的lazytag”叫做下传标记,“原有的lazytag”叫做原有标记。

下传标记是ass0时,清空所有原有标记再下传ass0(清空不是指进一步下传标记而是直接覆盖掉)。显然,不管前面积压了多少操作,推平以后都全是0,所以前面积压的lazytag已经无意义.

下传标记是ass1时同理,清空所有原有标记再下传ass1.

下传标记是rev时,如果原有ass0/ass1,那么直接把ass0变成ass1,或把ass1变成ass0即可(相当于交换两个标记)。这时下传下来的rev直接清空。

如果没有ass0/ass1,就直接把原有rev异或一下。(反转再反转相当于没动,所以是异或而不是直接改成true.)

lazytag和数据之间

消除当前点的lazytag(也就是push_down)时,各数据也需要相应变化。

len位区间长度。

消除ass0时,sum1清零,left0,right0,cont0改成lenleft1,right1,cont1清零。显然。

消除ass1时,sum1改成lenleft0,right0,cont0清零,left1,right1,cont1改成len

消除rev时,sum1变成lensum1(01互换), left0,left1,right0,right1,cont0,cont1互换。(这也是为什么需要额外计算left0,right0,cont0),否则反转的时候没法维护了。

数据的计算

各数据从下往上更新的方法。

sum1:两儿子sum1的和。

left0:如果左儿子区间全是0,那么答案就是左右儿子left0的和;否则仅为左儿子的left0的值。

left1:同理。

right0:如果右儿子区间全是0,那么答案就是左右儿子left0的和;否则仅为右儿子的left0的值。

right1:同理。

cont0:左右儿子的cont0,左儿子的right0+右儿子的left0这三个值取max。

cont1:同理。

Extra

在区间连续1查询中,方式和计算cont1相似而不同。需要考虑有可能当前区间内的连续1并不完全在查询的区间内,需要分别算出左右端点进行处理。


经过了以上繁琐的操作,我们就容易得到下面的代码了。

Code

能够完全理解并写出来这道题(并且debug完)证明线段树的各种操作基本上掌握的很透彻了。

p.s. 这题debug真的很搞人心态。

---------------------------------这是一片分界线---------------------------------

Problem 5

P3384 【模板】重链剖分/树链剖分(树剖也用了线段树...好像没毛病哈


看情况讲不讲罢。

Code

posted @   Cerebral_fissure  阅读(21)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示