JOISC2019 穿越时空 Bitaro

莫名其妙成为周更博主……


在后文中只考虑询问起点在终点左边的情况,右边的情况 reverse 即可,相等的情况是平凡的。

做一个小小的转化:把涉及第 (i) 个点的时间均减去 (i),一条连接 (i)(i+1) 的道路因为从 (i)(i+1) 所以被第 (i) 个点涉及。这样左右移动不会让时间 (+1)

再考虑:从起点到终点的移动过程一定类似于:先从起点开始一直往右跑直到撞到一个位置不能跑,然后调时间调到这个位置能够移动的时间的上界或者下界,然后继续跑。所以对于无修改的情况可以对于每一个位置的可向右移动时间的上下界维护倍增数组表示从这个位置开始向右撞了 (2^k) 次之后会到达哪一个位置的哪个端点。每次询问先二分找到第一个撞的位置,然后倍增处理出路径和贡献。


但是上面的做法显然无法拓展到有修改的情况。带修改、区间查询考虑使用线段树,那么我们需要得到一段路径的信息。那么我们能不能使用一个时间区间来代替一段路径的信息呢?

考虑两个相邻的位置 (i)(i+1)。设 (i)(i+1)(i+1)(i+2) 之间的路径的时间区间为 ([L_i,R_i])([L_{i+1},R_{i+1}]),如果这两个区间有交,交为 ([p,q]),那么这两个区间与区间 ([p,q]) 是等效的,即可以使用区间 ([p,q]) 替代 (i)(i+2) 的路径。

如果无交咋办?可以发现如果无交那么这段路径可以表示为三元组 ((p,q,c)) 的形式,即初始时间无论如何都要先调到时刻 (p) 才能走,到达结束位置的时间为时刻 (q),在路径中会付出 (c) 的代价。举个栗子:如果 (R_i< L_{i+1}) 那么三元组可以表示为 ((R_i , L_{i+1} , 0)),而 (R_{i+1} < L_i) 时则可以表示为 ((L_i , R_{i+1} , L_i - R_{i+1}))。可以发现这样表示和原来的问题是等价的。

引入了三元组之后还要考虑三元组和二元组、三元组和三元组的合并,按照原则就是能走就走,走不动调时间。

因为合并是用一个二/三元组替代一段路径所以是有结合律的,可以用线段树维护区间信息,每一次做右端点拿出来对应区间把左右端点的贡献处理一下即可。复杂度 (O(Q log n))

原文地址:https://www.cnblogs.com/Itst/p/12356044.html