题解:P10327 [UESTCPC 2024] 汉诺塔排序问题
这也太难了!!!!!!
本题解参考了 GD 省集的官方题解,但是官方题解过于晦涩难懂了,这是一个(可能)说人话的版本。
下文中的
考虑将整个过程倒过来:一开始,
不难发现,如果
定义
不妨假设我们要将
我们称一次这样的操作为基本操作。对于一次挪动了
下图是一次基本操作的示例。
考虑如何快速维护移动过程中的状态。观察到,一次基本操作对
因此,我们可以尝试使用一个“虚栈”
假设
考虑如下 Hack:
考虑前两步,如果每轮都只操作“必须操作的位置”(即图 1),那么第二轮需要重新挪动
1,2 两个圆盘,最终比第一步操作到圆盘4 (图 2)多一次操作。
因此,我们不仅有可能新进行一次基本操作,还有可能“拓展”之前进行的基本操作。
我们不妨先不管这一点,观察基本操作对虚栈造成的影响。注意到,留在栈中的元素,其
也就是说,我们至少需要给虚栈中元素两个属性:
现在考虑可能拓展已有操作的情形,我们还要额外维护一个属性
弹栈时维护一个变量
不难发现,假设要拓展已有操作将栈顶元素上方(不含栈顶)元素清空,就相当于让
对于栈顶元素
先令
- 如果其
diff = \text{true} ,则意味着其前驱必须成为额外圆盘。此时,拓展已有操作的代价是cur+2(tag'-pos) ,将这轮的额外操作设为到nxt(pos) 为止的代价是\max\{0,2(n-i+1-pos)-1\} ,取。无论如何,操作完成之后tag' 都会变为pos 。 - 否则,则意味着我们可以暂时不用管该元素。
处理结束后,弹出栈顶。
弹栈结束后,考虑栈顶元素。
-
如果其
pos=pos_p :先令
tag' \gets \min\{tag', tag\} 。- 如果其
diff = \text{true} ,我们不用管,因为这意味着p 上方的元素已经清空了; - 否则,其前驱必须成为额外圆盘,处理方法同上。
处理结束后,弹出栈顶。
- 如果其
-
否则,相当于上面
diff = \text{false} 的情形,同样令其前驱成为额外圆盘。注意此时tag' 不会减小。
最后,由于基本操作将所有额外圆盘归并到了与
- 如果其
pos=pos_p-1 ,将diff \gets diff \oplus 1 。 - 否则
pos_p-1 位置对应的元素变得非平凡,插入一个pos = pos_p - 1, tag = tag', diff = 1 的新元素。
不难验证其正确性。
综上,我们解决了本题,时间复杂度