2020.4.18集训总结

线段树专题II,比I还是好点吧…I的T3T4太毒瘤


T1

[POI2015] KIN

传送门

这题思路其实非常的巧妙,至少我想不到
我们考虑从左到右枚举右端点,然后拿线段树储存每个点作为当前选的左端点的答案,最后取 max ⁡ \max max就是当前的答案
然后考虑如何更新答案,我们发现,对于当前右端点 i i i位置上的 a i a_i ai,在 a i a_i ai上一次出现的位置 [ l a s t + 1 , i ] [last+1,i] [last+1,i]之间答案都应该加上 a i a_i ai,飞速码完之后测了一发样例,WA了,为什么呢?

因为我们仔细读定义就会发现,对于在 l a s t last last以前的部分,他是不应该计算答案的,因为如果出现相同的一个都不算,所以我们应该把 [ l a s t 2 + 1 , l a s t ] [last^2+1,last] [last2+1,last]这一段都要减掉 a i a_i ai,emmmmmm l a s t 2 last^2 last2就是 l a s t last last l a s t last last

然后就好了嗯


T2

[HEOI2016/TJOI2016] 排序

传送门

这周集训的第二题,感觉比第三题难想

这道题其实有复杂度为 O ( n log ⁡ n ) O(n\log n) O(nlogn)的线段树分裂的做法,但是我太菜了不会,所以只能来水两个 log ⁡ \log log的做法了qwq

我们首先考虑这个区间如果只有 0 0 0 1 1 1怎么做,那么对于一个区间,如果是升序排序,那么就把区间里面所有的 0 0 0扔到左边, 1 1 1扔到右边,其实就是一个简单的区间覆盖,降序也同理

如果不是 0 , 1 0,1 0,1怎么办呢?考虑把他们变成 0 , 1 0,1 0,1序列,二分一下,把所有小于 Δ \Delta Δ的走变成 0 0 0,其他的变成 1 1 1,然后如果最后 q q q的位置是 0 0 0,就尝试把 Δ \Delta Δ减小(因为 0 0 0太多了…),否则就尝试把 Δ \Delta Δ增大

这样我们就可以在 O ( n log ⁡ 2 n ) O(n\log^2n) O(nlog2n)的时间解决,因为 n ≤ 1 0 5 n\leq 10^5 n105,时限 4 s 4s 4s,显然可以通过


T3

[HAOI2012] 高速公路

传送门

先说老师给出的做法吧
显然这题要求的就是区间所有情况的和,因为边不好判断,所以可以把边看成点,那么对于查询一段看成点之后的区间,我们要求的应该就是 ∑ i = l r v i t i \sum_{i=l}^r v_it_i i=lrviti,其中 t i t_i ti表示第 i i i条边被访问的次数
根据乘法原理,就是左边的点数 × \times ×右边的点数,注意因为看成了点,所以 i i i点位置其实左边有一个右边有一个都要被算进去,所以我们知道了我们要求的是

∑ i = l r ( i − l + 1 ) ( r − i + 1 ) v i = ∑ i = l r ( r i − i 2 + i − l r + l i − l + r − i + 1 ) v i = ∑ i = l r ( − i 2 + ( l + r ) i + ( − l r − l + r + 1 ) ) v i = − ∑ i = l r i 2 ⋅ v i + ( l + r ) ∑ i = l r i ⋅ v i + ( − l r − l + r + 1 ) ∑ i = l r v i \begin{aligned} &\quad\sum_{i=l}^r (i-l+1)(r-i+1)v_i \\ &=\sum_{i=l}^r(ri-i^2+i-lr+li-l+r-i+1)v_i \\ &=\sum_{i=l}^r(-i^2+(l+r)i+(-lr-l+r+1))v_i \\ &=-\sum_{i=l}^r i^2\cdot v_i+(l+r)\sum_{i=l}^r i\cdot v_i+(-lr-l+r+1)\sum_{i=l}^r v_i \end{aligned} i=lr(il+1)(ri+1)vi=i=lr(rii2+ilr+lil+ri+1)vi=i=lr(i2+(l+r)i+(lrl+r+1))vi=i=lri2vi+(l+r)i=lrivi+(lrl+r+1)i=lrvi
然后算到这里就很明显了,我们需要维护的是区间的 ∑ i 2 ⋅ v i , ∑ i ⋅ v i \sum i^2\cdot v_i,\sum i\cdot v_i i2vi,ivi ∑ v i \sum v_i vi
然后维护这几个玩意也不难,就不说了

还有一种做法在这里


T4

[NOI2007]项链工厂

传送门

一个一个操作来说吧,先不考虑旋转的情况
显然是一个线段树维护老司机树也行,维护总段数,左颜色,有颜色

  • s w a p swap swap
    先查询出 x , y x,y x,y的颜色,然后再交换涂色,线段树单点查询单点修改
  • p a i n t paint paint
    线段树区间覆盖
  • c o u n t count count
    查询 [ 1 , n ] [1,n] [1,n],然后如果 l e f t c o l o r = r i g h t c o l o r ∨ s e g m e n t > 1 leftcolor=rightcolor \lor segment>1 leftcolor=rightcolorsegment>1,就答案 − 1 -1 1就好了
  • c o u n t s e g m e n t countsegment countsegment
    区间查询

然后考虑如果有旋转情况怎么办

  • r o t a t e rotate rotate
    开一个变量记录一下转了多少就好了
  • f l i p flip flip
    开一个变量记录有没有反转就好了

但是!!! r o t a t e + f l i p rotate+flip rotate+flip就变得极其恶心,细节不少
我们开一个变量 r o t rot rot记录旋转的量, r e v rev rev表示有没有被反转过

r e v = 0 rev=0 rev=0的时候,即没有被反转过,那么就只需要考虑 r o t rot rot,那么位置 i i i对应的就是 i − r o t i-rot irot(当然要取模之类的)
r e v = 1 rev=1 rev=1的时候,我们应该先反转,然后再按上面 r e v = 0 rev=0 rev=0的时候进行求值就可以了

但是!!!这里还有一个问题,就是如果 f l i p flip flip了之后,顺时针的方向也变了,也就是说,在 r e v = 1 rev=1 rev=1的时候,我们还需要把 l , r l,r l,r两个位置交换一下,然后再进行 p a i n t paint paint或者 c o u n t s e g m e n t countsegment countsegment


总结:
线段树很多时候并不处于主导作用,比如T1,T2,只是用来优化时间复杂度的嗯
我觉得各个题的实现的障碍在于

  • T1:确定线段树维护什么量,还有 [ l a s t 2 + 1 , l a s t ] [last^2+1,last] [last2+1,last]要减的问题虽然过不了样例
  • T2:想到 01 01 01做法并从 01 01 01做法推广到一般也就是二分
  • T3:推式子能力或者对于情况的逐步讨论
  • T4:思路不难,但是代码实现细节较多
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值