题解 LOJ3278 「JOISC 2020 Day3」收获

感谢 myt 神仙教会了我树状数组!Orz!

果树和人是相对运动的,因为是对人做询问,所以可以考虑让人不动,果树运动。这样的好处是可以把人的结构固定下来,便于用数据结构维护。

具体来讲,对于所有\(i\in[1,n]\),我们从第\(i\)个人向逆时针方向第一个到他的距离\(\geq C\)的人\(j\)连边。表示某棵果树被\(i\)摘掉后,下一个摘的人是\(j\)。边权是这两次采摘的时间间隔。

对于每棵果树,向它逆时针方向的第一个人连边。表示它的果子第一次是被这个人摘到的。边权是这个人到这棵果树的距离。

因为每个点都有且仅有一条出边,于是,我们得到了一个基环内向树森林。其中,每棵果树是一个叶子,其他节点都是人。开始时,果树会从它所在的叶子出发,经过每条边需要一定的时间。我们要回答\(Q\)次询问,问在某一时刻之前某个节点共被果树经过了多少次(同一棵果树如果多次经过同一节点,则每次都要被算在答案里)。

先把询问离线下来。给每个节点开一个vector,存关于这个节点的询问。

基环树森林里的每棵基环树显然相互独立。我们对分别对每棵基环树计算答案。

对于一棵基环树,我们先断掉它环上的一条边。然后分两种情况讨论:

  • 果树到达被询问节点时,之前没有经过被断掉的这条边。
  • 果树到达被询问节点时,之前经过了被断掉的这条边。注意,因为是在环上,所以有可能多次经过。

对于第一种情况,答案显然是树上(基环树断掉一条边后会变成普通的树)某个子树内,深度不超过某个值的果树数量。用dfs序把“子树”转化成一段区间,问题变为简单的二维数点问题。具体来说,我们把所有的果树和询问放在一起,按深度排序。每次加入一个点,或询问一个区间内的点数。可以用树状数组维护。

对于第二种情况。我们先把每棵果树移动到被断掉的这条边的终点。对于第\(i\)棵果树,设它移动到这个点所花费的时间为\(t_i\)。设要回答的询问时间为\(T\)。则对答案的贡献是\(\sum_{t_i\leq T}\frac{T-t_i}{len}\)。其中\(len\)是基环树的环长。这里的除法取整是一个细节。如果余数大于等于被断掉的边的终点被询问的节点的距离,则上取整,否则下去整。设这个距离为\(d\),则我们也可以形式化地把这个式子写作:\(\sum_{t_i\leq T}\lfloor\frac{T-t_i+len-d}{len}\rfloor\)

其实这也是一个二维数点问题。先把果树和询问一起按时间排序。我们可以用两个变量\(num,sum\),分别记录已经扫描到的果树的数量,和果树的\(\lfloor\frac{t_i}{len}\rfloor\)之和。对于某个查询\(x=T+len-d\),先令答案等于\(\lfloor\frac{x}{len}\rfloor\cdot num-sum\)。这样会多计算的是\(t_i\bmod len\)大于\(x\bmod len\)的这些果树(每棵这样的果树会使答案被多算\(1\))。我们事先把所有余数离散化一下,然后在树状数组上做单点修改、后缀和查询即可。

时间复杂度\(O(n\log n)\)

参考代码

posted @ 2020-03-27 18:02  duyiblue  阅读(620)  评论(2编辑  收藏  举报