浅谈一类【变色地砖】问题
jung_le666 · · 算法·理论
似乎没有人仔细搞过这个。更可能的原因是我起的名字和别人不一样。
就这样吧。
碰到了很多次,所以写一下。真的很浅。
保证 AI 贡献小于等于
问题情景
有
- 离开地砖,碰到就可以结束。
- 方向
0 地砖,会沿着某个方向前进到下一个地砖。 - 方向
1 地砖,会沿着某个方向前进到下一个地砖。 - ...
- 方向
w-1 地砖,会沿着某个方向前进到下一个地砖。
其中
这里的方向
问题可能是:
- 从某个点出发会走多少步,从哪个离开地砖离开,或者判断根本不能走出。
- 进行一次或多次行走,某个或者每个地砖被走多少次。或者说地砖的形态。
- 进行一次或多次行走,某种地砖最后有多少个。
约定的名称
我们称
特别的,我们规定,对于离开地砖,
我们称
特别的,我们规定,对于离开地砖,
我们称
我们称非离开地砖为普通地砖。
我们称
我们称
一些性质
通用
将点按照
这个排列的任意一个前缀可以有一个状态
有一个略不严谨的证明,你能发现他的漏洞吗?
:::error[略不严谨的证明]{open}
假设我们当前处于状态
:::warning[问题]
上面的证明已经可以理解了,但是有一点没有说清楚。
我们可以递归到一个点,是因为我们发现这个点的状态改变了,和上个问题一致,于是可以递归。
但是有一种可能,其中有一个点的所有出边全是一个点,于是这个点的状态没有改变,我们还有理由递归问题吗?
:::
:::success[解决]
请先看【问题】部分。
其实是能的。我们发现虽然状态没有变,但是我们照样遍历了这个点的所有出边,本质上也是递归了。
事实上,即使不那么特殊,只是涉及了多个,只要某个点被路过至少一次,他就会被路过至少
:::
这个性质好像用到的不多,但确实有用到这个的,用到即王炸。因此我常常把这种题想偏到这里。
序列变色问题
我们可以轻松将序列从离开地砖拆开来,然后问题可以视为离开地砖出现且仅出现在序列两边的情况。
方便起见,称方向 L,方向 R。离开地砖因为出现且仅出现在序列两边,所以忽略。
性质 1
对于只有一个点在上面动的情况,如果到达了某个点
这是显然的,因为人离开
这个东西很显然,但是可以让我们得出更多的性质。
性质 2
我们每次转向都是在起始位置左边的 R 和右边的 L,同时每次都会在较接近的转折点转向,那边转向的多,就会在对面的离开地砖离开。
显然。
性质 3
假设起始点在 L 或右边的 R 变换。显然至少有一个可行。
证明简单,假设左边有 L,R。那么我们会转 L 改成 R。另外一边类似。
这个就比较有用了。
性质 4
本质上是性质三的特殊情况。如果初始序列较特别,为 RRR...RRRLLL...LLL,那么有更多性质。
首先声明一些东西:
每次从某个点开始行走,都会将一个前缀改成 R 或者把一个后缀改成 L。因此无论进行多少次操作序列始终为 RRR...RRRLLL...LLL 的结构,可能全为某种字符,但无伤大雅。我们用 R 的个数来表达这个序列。显然
然后在
这个已经对了,但是不够好,可以稍微美化亿下。
然后我们发现……
这真是一个有用的性质。
例题
P13336 [GCJ 2012 Finals] Shifting Paths - 洛谷
首先这是一个普通变色地砖问题,比较困难,直接的想法是考虑暴力。复杂度
然后我们想要 meet in middle,这样复杂度说不定变成
结果你发现失败了。由于
然后你敏锐注意到#通用|通用性质,于是你考虑把
复杂度是
提交记录 实现
CF733E Sleep in Class - 洛谷
序列变色地砖简单题。
走一步就花一秒。即求每个位置独立出发,走过的格子数。
根据性质 2,我们仅会在转折点变向,其他地方都是一路畅通的。
所以你的路线一定长这样(其他情况类似):
所以我们只考虑端点的贡献即可。
那么我们要怎么求所有点的答案呢?
首先我们可以快速地判断出来一个点出发是从左边出去的还是右边出去的,然后分别处理。
不妨处理左边出去的。
然后我们发现其代价核心的部分在于转折点的贡献。
左边的贡献是简单的,容易发现如果从左往右扫,将会不删。于是直接继承上次计算的东西即可。或者直接预处理也行。
右边处理也相对简单,容易发现产生贡献的东西是个区间,而左端点和右端点都不降,所以可以双指针。
提交记录/实现
P9353 [JOI 2023 Final] 现代机器 / Modern Machine - 洛谷
这题太难了,不会,留给读者当课后练习。
其中特殊性质启发我们使用#性质 4,但是一般情况没有这个性质。
:::info[特殊说明]
本题有要求放入时强制覆盖,所以需要调整。
结果是:
:::
但我们发现很多时候由于我们每次染一个前缀染一个后缀,序列很可能马上就会变成 RRR...RRRLLL...LLL 的特殊结构。
显然这个序列一定是前缀被染成 R,后缀被染成 L,可能有中间保持原状。
接着你使用#性质 3,发现能把点落在前后缀上的情况快速做掉。
然后你又发现,如果没落在中间,被染色的无论是前缀还是后缀都会翻倍。
所以,中间的变换是
但是你发现维护前缀和后缀太难了,一直在变,且总数巨大。于是我们考虑偷一点懒,不严格维护前后缀大小,而是维护略小一点的
之后就是一些预处理加速和复杂度证明的事情了,和变色地砖关系不大了。
实现貌似有很多细节,所以没写。