[lnsyoj2144/luoguP1852] 跳跳棋
题意
在数轴上,给定 个点,每个点可以在不越过两个点的前提下以另一点为中心进行轴对称,且两点不能重合,求能否变为给定目标状态及最少步数
sol
我们发现,由于不能越过两个点,因此本题只有四种移动方式(下设三点坐标分别为 )
- 以 为中心向左跳;
- 以 为中心向右跳;
- 以 为中心向右跳();
- 以 为中心向左跳()。
容易注意到,操作 、 是互斥的,因此最多只会有 种情况,因此可将操作 / 作为根,操作 、 作为左右儿子,形成决策树森林,特别地,当 时,不存在操作 /,因此不存在父亲,是某一棵决策树的根。
问题转化为求森林中树上两个点的距离,若不在一棵树上,则无解。
此时仍然无法通过,不太容易注意到,我们并不需要建出整个森林,而是可以将各操作(深度计算,上跳,LCA,找根)都抽象为数学计算。
找根/深度计算
(下设 )
在操作 / 中,列出 与 ,可以得出 或 ,即更相减损法计算 ,因此可以通过魔改欧几里得算法来解决找根问题,具体地,将 改为 ,这会对深度产生 的贡献, 变化同理。
需要注意,当到达根节点时,由于取模原因,会出现 或 的情况,即两点重合,这种情况不可能发生,因此还需要下移一位,并使 。
上跳
参照找根,但在取模产生的上跳操作超过剩余的步数 时,改为较大值减去较小值的 倍。
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);
}
分类:
题解 / 2025赛时
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现