Solution -「洛谷 P1852」跳跳棋
Description
Link.
在一个数轴上给你三个点,移动方法是彼此为中点进行跳跃,不能同时越过两颗棋子。
给出初始状态和目标状态,问能否从初始状态跳到目标状态。若能,输出最少步数。
棋子之间互相没有差别。
Solution
这道题是我们去年学倍增的时候LF给我们放的一道题(实际上和倍增没有半毛钱的关系。
啊对我就是一道题从去年做到今年(我不管做出来了就是我的题量XD/xyx
不扯了说正事儿。
其实拿到这道题我是很懵的,完全不知道该怎么入手。
然后我们拿出传统手艺手玩数据。我们可以发现对于一个三元组 \((x,y,z)\) 只有三种移动的方案:
(啊对了,说一下这里的三元组表示的是一种位置关系,即 \(x\) 在左 \(y\) 在中间 \(z\) 在右)
中间往两边跳
- \(y\) 以 \(x\) 为中点进行跳跃,三元组变为:\((x-(y-x),x,z)\implies(2x-y,x,z)\)
- \(y\) 以 \(z\) 为中点进行跳跃,三元组变为:\((x,z,z+(z-y))\implies(x,z,2z-y)\)
两边往中间跳
这里方便讨论我们不妨设 \(dis_{1}\) 为 \(x\) 和 \(y\) 之间的距离,\(dis_{2}\) 为 \(y\) 和 \(z\) 之间的距离。
当 \(dis_{1} > dis_{2}\)
- \(x\) 以 \(y\) 为中点进行跳跃,三元组变为:\((y,y+dis_{1},z)\implies(y,2y-x,z)\)
当 \(dis_{1} < dis_{2}\)
- \(z\) 以 \(y\) 为中点进行跳跃,三元组变为:\((x,y-dis_{2},y)\implies(x,2y-z,y)\)
当 \(dis_{1}=dis_{2}\)
无法继续跳,也就是我们的边界。
好,现在把所有我们已知的条件串起来看:这不就是一棵二叉树吗?
对呀,这就是一棵二叉树(我在说什么
为什么这一定是一棵树呢?想一想就明白了,就像数学中的收敛一样,最后一定会出现 \(dis_{1}=dis_{2}\) 的情况。
而且这棵树是唯一确定的。
那么这道题的答案是什么就很明确了。
首先判断YES或NO的情况我们只需要判断初始状态和目标状态是否在统一棵状态树上即可。
最小的步数就是他们两个在树上的距离。
暴力肯定是不可取的。
但是我们可以通过模拟暴力的情景来得到优化的策略。
比如我们思考一个数据:\(x,y,z\)。
其中 \(y\) 是一个 远大于 \(x\) 的数。
方便起见我们令 \(z=y+1\)
暴力此时会不停的让 \(z\) 以 \(y\) 为中点跳。
此时我们回顾一下题面:
棋子之间互相没有差别。
没错,每颗棋子之间其实是没有任何差别的。
打个比方说,现在 \(z\) 以 \(y\) 为中点进行了跳跃,其本质就是 \(z\) 和 \(y\) 一起往左平移了 \(z-y\) 步。
这就意味着,我们不需要老老实实的每次都跳,我们可以直接获得 \(z\) 跳跃的次数即 \((y-x)\div(z-y)\) 次。
再推一下就是欧几里得最大公约数的形式了,也就是说我们在 \(log_{2}\) 的复杂度解决了这个问题。
最后二分一下到LCA的距离,这道题就被你暴切啦
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <queue>
using namespace std;
int a, b, c;
int x, y, z;
void sort(int& x, int& y, int& z) {
vector < int > vec;
vec.push_back(x);
vec.push_back(y);
vec.push_back(z);
sort(vec.begin(), vec.end());
x = vec[0], y = vec[1], z = vec[2];
}
void Initialization() {
scanf("%d %d %d", &a, &b, &c);
scanf("%d %d %d", &x, &y, &z);
sort(a, b, c);
sort(x, y, z);
}
int GetRoot(int& x, int& y, int& z) {
int dis1 = y - x;
int dis2 = z - y;
if (dis1 ^ dis2) {
int temp;
if (dis1 < dis2) {
temp = dis2 / dis1;
if (!(dis2 % dis1)) --temp;
x += temp * dis1;
y += temp * dis1;
}
else {
temp = dis1 / dis2;
if (!(dis1 % dis2)) --temp;
z -= temp * dis2;
y -= temp * dis2;
}
return temp + GetRoot(x, y, z);
}
else return 0;
}
void ArriveK(int& x, int& y, int& z, int key) {
int dis1 = y - x;
int dis2 = z - y;
if (dis1 < dis2) {
int temp = dis2 / dis1;
if (!(dis2 % dis1)) --temp;
if (temp >= key) {
x += key * dis1;
y += key * dis1;
}
else {
x += temp * dis1;
y += temp * dis1;
ArriveK(x, y, z, key - temp);
}
}
else {
int temp = dis1 / dis2;
if (!(dis1 % dis2)) --temp;
if (temp >= key) {
z -= key * dis2;
y -= key * dis2;
}
else {
z -= temp * dis2;
y -= temp * dis2;
ArriveK(x, y, z, key - temp);
}
}
}
signed main() {
Initialization();
int a0 = a, b0 = b;
int c0 = c, x0 = x;
int y0 = y, z0 = z;
int rt1 = GetRoot(x0, y0, z0);
int rt2 = GetRoot(a0, b0, c0);
if (a0 ^ x0 || b0 ^ y0 || c0 ^ z0) return puts("NO") & 0;
if (rt1 < rt2) ArriveK(a, b, c, rt2 - rt1);
else ArriveK(x, y, z, rt1 - rt2);
int l = 0, r = min(rt1, rt2);
while (l < r) {
int mid = (l + r) >> 1;
a0 = a, b0 = b, c0 = c;
x0 = x, y0 = y, z0 = z;
ArriveK(x0, y0, z0, mid);
ArriveK(a0, b0, c0, mid);
if (x0 == a0 && y0 == b0 && z0 == c0)
r = mid;
else
l = mid + 1;
}
printf("YES\n%d\n", max(rt1, rt2) - min(rt1, rt2) + (l << 1));
return 0;
}
Solution -「洛谷 P1852」跳跳棋的更多相关文章
- Solution -「洛谷 P4372」Out of Sorts P
\(\mathcal{Description}\) OurOJ & 洛谷 P4372(几乎一致) 设计一个排序算法,设现在对 \(\{a_n\}\) 中 \([l,r]\) 内的元素排 ...
- Note/Solution -「洛谷 P5158」「模板」多项式快速插值
\(\mathcal{Description}\) Link. 给定 \(n\) 个点 \((x_i,y_i)\),求一个不超过 \(n-1\) 次的多项式 \(f(x)\),使得 \(f(x ...
- Solution -「洛谷 P4198」楼房重建
\(\mathcal{Description}\) Link. 给定点集 \(\{P_n\}\),\(P_i=(i,h_i)\),\(m\) 次修改,每次修改某个 \(h_i\),在每次修改后 ...
- Solution -「洛谷 P6577」「模板」二分图最大权完美匹配
\(\mathcal{Description}\) Link. 给定二分图 \(G=(V=X\cup Y,E)\),\(|X|=|Y|=n\),边 \((u,v)\in E\) 有权 \(w( ...
- Solution -「洛谷 P6021」洪水
\(\mathcal{Description}\) Link. 给定一棵 \(n\) 个点的带点权树,删除 \(u\) 点的代价是该点点权 \(a_u\).\(m\) 次操作: 修改单点点权. ...
- Solution -「洛谷 P4719」「模板」"动态 DP" & 动态树分治
\(\mathcal{Description}\) Link. 给定一棵 \(n\) 个结点的带权树,\(m\) 次单点点权修改,求出每次修改后的带权最大独立集. \(n,m\le10^5 ...
- Solution -「洛谷 P5236」「模板」静态仙人掌
\(\mathcal{Description}\) Link. 给定一个 \(n\) 个点 \(m\) 条边的仙人掌,\(q\) 组询问两点最短路. \(n,q\le10^4\),\(m\ ...
- Solution -「洛谷 P4320」道路相遇
\(\mathcal{Description}\) Link. 给定一个 \(n\) 个点 \(m\) 条边的连通无向图,并给出 \(q\) 个点对 \((u,v)\),询问 \(u\) 到 ...
- Solution -「洛谷 P5827」边双连通图计数
\(\mathcal{Description}\) link. 求包含 \(n\) 个点的边双连通图的个数. \(n\le10^5\). \(\mathcal{Solution}\) ...
- Solution -「洛谷 P5827」点双连通图计数
\(\mathcal{Description}\) link. 求有 \(n\) 个结点的点双连通图的个数,对 \(998244353\) 取模. \(n\le10^5\). \(\mat ...
随机推荐
- 现代 C++ 性能飞跃之:移动语义
*以下内容为本人的学习笔记,如需要转载,请声明原文链接微信公众号「ENG八戒」https://mp.weixin.qq.com/s/Xd_FwT8E8Yx9Vnb64h6C8w 带给现代 C++ 性能 ...
- ORM总览
ORM(Object-Relational Mapping)是一种常见的数据访问技术,它将对象模型和关系模型之间进行映射.ORM的主要作用是简化数据访问和管理,提高开发效率和代码质量.在实际应用中,O ...
- Rust函数参数传递的一个观点
Q5: 一个函数的观点A5: Rust中的每个函数都是自治的,在每一个函数体中,相当于重新开辟了一个新的领域.将参数传递给函数参数,与let声明一个绑定是一样的规则. 1 ``` 2 // 所有权语义 ...
- c++函数重载 c/c++混合编程
C++语言支持函数重载实现原理: 名字改编(name mangling)具体步骤: 当函数名称相同时,会根据函数参数的类型.个数.顺序进行改编 对源码直接用C++编译器进行编译时,会按C++方式进行调 ...
- 8. RESTful案例
1. 准备工作 和传统 CRUD 一样,实现对员工信息的增删改查. 搭建环境 准备实体类 package com.atguigu.mvc.bean; public class Employee { ...
- 逍遥自在学C语言 | 函数初级到高级解析
前言 函数是C语言中的基本构建块之一,它允许我们将代码组织成可重用.模块化的单元. 本文将逐步介绍C语言函数的基础概念.参数传递.返回值.递归以及内联函数和匿名函数. 一.人物简介 第一位闪亮登场,有 ...
- PostgreSQL 12 文档: 部分 VI. 参考
部分 VI. 参考 这份参考中的条目意欲提供关于相应主题的权威.完整和正式的总结.关于使用PostgreSQL的更多信息(以叙述.教程或例子的形式)可以在本书的其他部分找到.见每个参考页面上列出的交叉 ...
- Python与MySQL如何保持长连接
Python与MySQL如何保持长连接 介绍 在python后端开发中,时常会与数据库交互,重复的断开.连接 会大大消耗数据库资源. 所以一般都是定义全局变量,来弥补这个缺陷. 但是 Python 与 ...
- Unity UGUI的所有组件的介绍及使用
Unity UGUI的所有组件的介绍及使用 本文将介绍Unity UGUI中的各个组件,包括它们的具体介绍.用途 1. Text(文本) 介绍:Text组件用于在UI界面上显示文本内容. 用途:常用于 ...
- Python Django 模版全解与实战
本文首先介绍了Django模板系统的基础知识,接着探讨了如何安装和配置Django模板系统,然后深入解析了Django模板的基本结构.标签和过滤器的用法,阐述了如何在模板中展示模型数据,最后使用一个实 ...