CF2019D. Speedbreaker 题解
介绍一种另解,以下称“征服”为“拓展”。
对于这些需要拓展,且拓展的时间有上界的题,我们通常都会有一个 trick。那就是对于一个点 \(x\),用它可以拓展到的点 \(y\) 的时间上界把 \(x\) 的时间上界继续缩小。用到这种 trick 的题有 P9755 [CSP-S 2023] 种树、[ABC304Ex] Constrained Topological Sort,以及本题。
关于这个 trick 在本题的具体体现,不妨设 \(t_i\) 为点 \(i\) 被拓展到的时间,\(a_i\) 为点 \(i\) 被拓展时间的上界。定义 \(L_i\) 为从点 \(i\) 拓展到点 \(1\),满足 \(\forall i\in[1,i],t_i\leq a_i\) 的最大的 \(t_i\)。则 \(L_i\) 的转移式为:
这是因为从 \(i\) 拓展到 \(i-1\) 需要 \(1\) 的时间,且也要满足 \(t_i\leq a_i\)。
同理,定义 \(R_i\) 为从点 \(i\) 拓展到点 \(n\),满足 \(\forall i\in[i,n],t_i\leq a_i\) 的最大的 \(t_i\)。则:
容易发现 \(L_i\) 是单调递减的,\(R_i\) 是单调递增的。
求出 \(L_i\) 和 \(R_i\) 有什么用呢?考虑当前枚举了一个起始点 \(P\)。对于除了 \(P\) 外的所有点,设其权值为
贪心的想,要想满足每个点的时间上界,我们可以优先拓展那些 \(val\) 值小的点。
那这样思路就很明显了,就像 dijkstra 一样,每次把当前可以拓展的点扔进一个小根堆里,按 \(val_i\) 排序,每次从堆顶取出一个点就相当于拓展了这个点,并把时间计数器 \(cnt+1\),接着再把这个点可以拓展到且还未被拓展的点扔进堆里。
判无解就是判当一个点被从堆顶取出时,当前的时间计数器是否满足 \(cnt\leq val_i\),如果不满足,则说明点 \(P\) 不能作为起始点。
但这样做是 \(O(n^2)\) 的,考虑如何优化。
容易发现,对于一个起始点 \(P\),
这是为什么?考虑 \(i<P\) 的情况。对于求和前面的前面那一部分,如果想要拓展到 \(i\),则需先将 \([i+1,P+1]\) 的点扩展,因为开始在 \(P\) 时间就为 \(1\),所以要加 \(1\)。对于求和的部分,则表示在点 \(P\) 右边的需要比 \(i\) 先拓展的点的个数,因为 \(R_i\) 单调递增的性质,可以发现这些点为以 \(P+1\) 为开头的一段前缀,正确性显然。\(R_i\) 同理。
但是若是有 \(i<P,j>P,L_i=R_j\) 的情况呢?这样的情况放在暴力代码中,\(val_i\) 是等于 \(val_j\) 的,且它们一定是先拓展一个,再拓展另一个。也就是说,\(t_i+1=t_j\) 或者 \(t_j+1=t_i\),判断是否可行则是看 \(\max(t_i,t_j)\) 是否小于 \(val_i\)。而在上面的式子中,\(t_i\) 和 \(t_j\) 都会是 \(\max(t_i,t_j)\),而这对可行判断是没有影响的。
有了这个式子,我们如何快速判断可不可行?我们可以将 \(val_i\) 减去 \(t_i\)。若是不可行,则 \(\min\limits_{i=1\land i\ne P}^{n}\{val_i-t_i\}<0\),其实就是看存不存在 \(t_i>val_i\)。
然后用数据结构维护就行了,我这里用了两棵平衡树,一颗维护 \(i<P\) 的点,以 \(L_i\) 为排序关键字,一颗维护 \(i>P\) 的点,以 \(R_i\) 为排序关键字,其实就是 \(val_i\)。设其为 \(T_0\),\(T_1\)。
平衡树的一个节点 \(i\) 需要维护它的排序关键字 \(val_i\),\(val_i-t_i\) 的值,它所在子树的 \(\min\limits_{j\in subtree_i} \{val_j-t_j\}\)。且平衡树还要支持区间加减。设 \(sum_i=val_i-t_i\),\(minn_i=\min\limits_{j\in subtree_i} \{val_j-t_j\}\)。
枚举 \(P\) 时,每次 \(P\leftarrow P+1\) 时,将 \(P+1\) 从 \(T_1\) 中删除,并将 \(P\) 加入 \(T_0\),同时将 \(T_1\) 整棵树的 \(sum\) 减 \(1\),\(T_0\) 整棵树的 \(sum\) 加 \(1\)。还要将 \(P+1\) 在 \(T_1\) 时对 \(T_0\) 的贡献抹去,\(P\) 在 \(T_0\) 时对 \(T_1\) 的贡献加上,具体表现为将 \(T_0\) 中 \(val\) 大于等于 \(R_{P+1}\) 的节点的 \(sum\) 加 \(1\),\(T_1\) 中 \(val\) 大于等于 \(L_{P}\) 的节点的 \(sum\) 减 \(1\)。最后一边修改一边更新 \(minn\)。
如此,\(P\) 可以成为一个起始点当且仅当此时 \(T_0\) 整棵平衡树的 \(minn\) 大于等于 \(0\),\(T_1\) 整棵平衡树的 \(minn\) 也大于等于 \(0\)。

浙公网安备 33010602011771号