[Luogu]P5513 [CEOI2013]Board

\(Link\)

Description

Solution

首先根据手动模拟,可以发现对于两个节点\(A,B\),首先必须要花费一些代价向上跳到同一深度。

然后再让\(A,B\)同时向上跳,中途可能\(A,B\)再通过只走横向的边相遇。这样就可以维护最小值,求出答案了。

现在主要的问题就是要怎么写高精度。注意到我们需要维护一个初始值为\(1\)的数\(x\),并支持以下\(3\)种操作:

\(1.\)乘以\(2\)(并加上\(1\));

\(2.\)除以\(2\)并下取整;

\(3.\)加减\(1\)

所以可以用二进制高精,并将输入操作序列转化为只有向下移动的序列,这样就方便操作了。

我们维护一个数组 \(a_{1}, \cdots, a_{n},\) 表示当前数为 \(\sum_{i=1}^{n} a_{i} 2^{n-i}\)。(\(0\)是向左,\(1\)是向右)加减\(1\)的操作直接做,乘以\(2\)(并加上\(1\))的操作就把\(n\)加上\(1\),并让\(a_n=0/1\),除以\(2\)的就先维护进/退位,再把\(n\)\(1\)即可。处理完后,再从后往前维护一遍进/退位。

(处理进/退位:a[pos - 1] += ((a[pos] - (a[pos] & 1)) >> 1); a[pos] &= 1;

但是这样做为什么是对的呢?

注意到对\(a\)序列进行的操作,都唯一且恰好对应对数\(x\)(即当前节点编号)的操作。且因为我们维护了进位,所以通过\(a\)序列算出来的数值一定和\(x\)相等。从而这样是可以做的。

于是先把输入的操作序列转化为两个序列\(a,b\),再维护一个\(\Delta\)表示\(A,B\)(在同一层)的横向距离,手模可以发现\(\Delta = 2 * \Delta + a[i] - b[i]\)。再通过之前说的方法,既可以求出答案。

当然,如果计算过程中\(\Delta\)的值过大,可以直接\(break\)掉(这时肯定不会是答案)。

时间复杂度\(O(n+|S|)\)

posted @ 2020-10-22 10:39  andysj  阅读(67)  评论(0编辑  收藏  举报