P2486 [SDOI2011] 染色题解如何为?
摘要:P2486 题解 这里来提供一个思维难度小的做法。 貌似是暑假在一场多校中碰到过类似的题,看的大多数题解的思路都是维护一个多个信息的线段树或者是写珂朵莉树,我的代码只需要维护两个可以推平和求和的线段树,来写一发题解分享一下。 还是建议大家看
P2486 题解
这里来提供一个思维难度小的做法。
貌似是暑假在一场多校中碰到过类似的题,看的大多数题解的思路都是维护一个多个信息的线段树或者是写珂朵莉树,我的代码只需要维护两个可以推平和求和的线段树,来写一发题解分享一下。
还是建议大家看一下线段树维护多个信息这种做法,这种做法的应用范围更广。
题意
很清晰,一棵树,每个点有颜色。需要支持修改一两个点之间所有点的颜色和查询两个点之间颜色段的数量这两种操作。
思路
考虑一下如果没有修改那怎么做。
对于查询 \(u,v\) ,我们把其中的点分为两类来算贡献
\(LCA\) :一定属于一个新颜色段,它的贡献为 1
其他点 : 如果当前点与父亲节点颜色相同,那么它属于父亲节点所在颜色段。否则是一个新的颜色段,它的贡献为 1
具体我们只需要预处理出所有点的贡献,然后树上差分一下就能做。
下面来考虑带修怎么做。
如果一个点的颜色被修改,那么贡献会被它影响到的点就有它本身和它的所有儿子,显然这个做法会被菊花图卡的死死的,我们有什么办法来优化这个部分呢?
我们可以向树链剖分上面想,对于一个节点,如果它不是叶子节点,那么它会有一个重儿子,其他的都是轻儿子,为了保证复杂度,我只修改它的重儿子行不行呢?如果只修改重儿子,轻儿子的贡献应该怎么算呢?
做法应该呼之欲出了。
修改时,每次我们只更新颜色被更改的点的贡献与它的重儿子的贡献,由于是一段相同的颜色区间,所以真正需要修改的只有当前区间末尾的重儿子,其他点的贡献全都为 0 。
查询时,链头是轻儿子,链头是不会被更新的,所以我们手动比较它与它的父亲的颜色是否相同,不同的话就贡献 + 1,然后查这一整个区间的贡献,再向上跳。
分析一下复杂度,对于修改操作,每次需要推平整个区间,查询区间末尾点的重儿子的颜色以及更新重儿子的贡献,复杂度为 \(O( \log n )\) ,向上跳 \(O(\log n )\) 次,所以单次复杂度为 \(O(\log ^2n)\)
对于查询操作,每次需要查整个区间的贡献,查链头和链头父亲的颜色,复杂度为 \(O(\log n)\),向上跳 \(O (\log n )\) 次,单次复杂度为 \(O( \log ^2n)\)
所以复杂度为 \(O( n \log ^2 n)\)
我们只需要一个线段树存颜色另一个存贡献就行了。
为了减少码量我封装到一起了。
欢迎进行提问,如果有不足之处,欢迎指教。
