[lnsyoj2144/luoguP1852] 跳跳棋
题意
在数轴上,给定 \(3\) 个点,每个点可以在不越过两个点的前提下以另一点为中心进行轴对称,且两点不能重合,求能否变为给定目标状态及最少步数
sol
我们发现,由于不能越过两个点,因此本题只有四种移动方式(下设三点坐标分别为 \(a,b,c\))
- \(b\) 以 \(a\) 为中心向左跳;
- \(b\) 以 \(c\) 为中心向右跳;
- \(a\) 以 \(b\) 为中心向右跳(\(c-b>b-a\));
- \(c\) 以 \(b\) 为中心向左跳(\(c-b<b-a\))。
容易注意到,操作 \(3\)、\(4\) 是互斥的,因此最多只会有 \(3\) 种情况,因此可将操作 \(3\)/\(4\) 作为根,操作 \(1\)、\(2\) 作为左右儿子,形成决策树森林,特别地,当 \(c-b=b-a\) 时,不存在操作 \(3\)/\(4\),因此不存在父亲,是某一棵决策树的根。
问题转化为求森林中树上两个点的距离,若不在一棵树上,则无解。
此时仍然无法通过,不太容易注意到,我们并不需要建出整个森林,而是可以将各操作(深度计算,上跳,LCA,找根)都抽象为数学计算。
找根/深度计算
(下设 \(s1=b-a,s2=c-b\))
在操作 \(3\)/\(4\) 中,列出 \(s1\) 与 \(s2\),可以得出 \(s1=s1-s2\) 或 \(s2=s2-s1\),即更相减损法计算 \(gcd\),因此可以通过魔改欧几里得算法来解决找根问题,具体地,将 \(s1=s1-s2\) 改为 \(s1=s1\%s2\),这会对深度产生 \(\lfloor s1/s2\rfloor\) 的贡献,\(s2\) 变化同理。
需要注意,当到达根节点时,由于取模原因,会出现 \(s1=0\) 或 \(s2=0\) 的情况,即两点重合,这种情况不可能发生,因此还需要下移一位,并使 \(s1=s2\)。
上跳
参照找根,但在取模产生的上跳操作超过剩余的步数 \(step\) 时,改为较大值减去较小值的 \(step\) 倍。
LCA
参照普通 LCA,使两点位于同一深度,然后二分答案上跳的步数即可。
需要注意不保证给定的两组点有序。
代码
#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;
struct Node {
int a, b, c;
int dep;
bool operator== (const Node &W) const {
return a == W.a && b == W.b && c == W.c;
}
void sort(){
int na = a, nb = b, nc = c;
a = min(na, min(nb, nc));
c = max(nc, max(na, nb));
b = na + nb + nc - a - c;
}
Node find_root() {
int s1 = b - a, s2 = c - b;
int x = a, y = b, z = c;
dep = 0;
while (s1 != s2) {
// printf("#%d %d %d %d %d %d %d %d %d\n", a, b, c, x, y, z, s1, s2, dep);
if (s1 > s2) {
dep += s1 / s2;
s1 = s1 % s2;
if (!s1) s1 = s2, dep -- ;
y = x + s1, z = y + s2;
}
else if (s1 < s2) {
dep += s2 / s1;
s2 = s2 % s1;
if (!s2) s2 = s1, dep -- ;
y = z - s2, x = y - s1;
}
}
return {x, y, z, 0};
}
Node jump(int step){
int fdep = dep - step;
if (fdep <= 0) return find_root();
int s1 = b - a, s2 = c - b;
int x = a, y = b, z = c;
while (step > 0) {
if (s1 > s2) {
int delta = s1 / s2;
if (step >= delta) s1 = s1 % s2;
else s1 -= step * s2;
y = x + s1, z = y + s2;
step -= delta;
}
else {
int delta = s2 / s1;
if (step >= delta) s2 = s2 % s1;
else s2 -= step * s1;
y = z - s2, x = y - s1;
step -= delta;
}
}
return {x, y, z, fdep};
}
} st, ed;
bool check(int mid){
Node sx = st.jump(mid), sy = ed.jump(mid);
return sx == sy;
}
int main(){
scanf("%d%d%d%d%d%d", &st.a, &st.b, &st.c, &ed.a, &ed.b, &ed.c);
st.sort(), ed.sort();
Node rst = st.find_root(), red = ed.find_root();
if (!(rst == red)) return puts("NO"), 0;
if (st.dep < ed.dep) swap(st, ed);
int ans = st.dep - ed.dep;
st = st.jump(st.dep - ed.dep);
int l = 0, r = st.dep;
while (l < r){
int mid = l + r >> 1;
if (check(mid)) r = mid;
else l = mid + 1;
}
ans += 2 * l;
printf("YES\n%d\n", ans);
}

浙公网安备 33010602011771号