AtCoder abc158_f

大前提:这是一道简单题,难度只有 2000(

AtC 题目页面传送门

(i) 个机器人,第 (i) 个坐标为 (x_i),激活后移动距离为 (d_i)。一开始全未激活,你可以激活若干个,第 (i) 个激活后,所有坐标在 ([x_i,x_i+d_i)) 内的机器人都会被激活。问有多少种可能的被激活的机器人集合。答案对 (998244353) 取模。

(ninleft[1,2 imes10^5 ight])

首先第一反应是建图,将每个机器人连向它可以激活的所有机器人。但是这样光建图暴力就平方了。然而我们可以用线段树优化。但是建出来图之后要求传递闭包,不可能行。所以必须避开建图这一想法。

那显然,建图不能做那它肯定有特殊性质可以用更简单的方法做。注意到一个显然且有用的性质:将机器人按坐标排序后,每个机器人的传递闭包都是个区间。那我们就可以用类似 DP 的方法求了。

(rit_i) 表示机器人 (i) 的传递闭包的右端点(左端点显然是 (i)),那么就与 (i) 和所有的 (rit_j)(j) 能被 (i) 到达)取个 (max)。又显然,(j) 组成的也是区间,于是我们可以动态 RMQ。比较傻的方法是线段树,然而 hb 让我们不要养成线段树依赖症。注意到这里是每次相当于在序列开头加一个数,然后查询一次,不难想到 ST 表是可以在开头结尾 push 的,于是写个动态 ST 表可以有效减少码量。

然而还有更 nb 的方法。注意到从右往左递推的过程中,若一个机器人被它左边的机器人算进去了,那么此机器人就再也发挥不了用处了,因为算它进去的机器人已经覆盖它的所有影响效果了。根据这个性质我们可以及时删除无用决策,使用单调栈。具体就是每次算的时候弹出所有栈顶可以转移到的机器人转移,然后 pop 掉,然后再把本机器人 push。这样每个机器人进出一次,线性。

然后就是求出来 (rit) 之后怎么求答案了。这个就异常简单了,随便 DP 就行了。注意到最终的一种情况一定可以分解成若干个选的区间和不选的区间错落排列,于是 (dp_{i,0/1}) 表示前 (i) 个中,第 (i) 个是选 / 不选的方案数。转移就随便前缀和即可,左端点随便单调栈一下。

总复杂度肯定是线性对数的,无论怎么优化排序还是要排的。

由于我比较无聊为了稍微练一下不常用的 DS,我把求 (rit) 的三种方法都写了一遍,代码太长了就只放链接了。

珍爱生命,远离抄袭!
原文地址:https://www.cnblogs.com/ycx-akioi/p/AtCoder-abc158-f.html