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 ...
随机推荐
- 为teamcity的代码语法检查工具pyflakes增加支持python2和python3
TeamCity和pyflakes TeamCity是一款由JetBrains公司开发的持续集成和部署工具,它提供了丰富的功能来帮助团队协作进行软件开发.其中包括代码检查.自动化构建.测试运行.版本控 ...
- R 语言 download.file 的几点知识
R 语言中,不管是安装包,还是下载数据,很多时候都会用到download.file这个函数.如果你在安装包或者下载数据过程中出现中断,或者异常,想要判断是远程源服务器的问题,还是自身服务器的问题,还是 ...
- Kali安装GVM
1.安装gvm ┌──(rootkali)-[/home/kali] └─# gvm-setup 1 ⨯ [>] Starting PostgreSQL service [-] ERROR: T ...
- 搭建私人GPT及域名配置
前几天在掘金看到一个搭建私人ChatGPT的教程,看起来并不难. 我也有OpenAI的API Key,然后前阵子我看到我的账号余额还有很多,我的api key其实就一个机器人在用,没用多少. 还有,就 ...
- 基于Node.js的分布式应用程序架构设计与最佳实践:实现高效、可扩展的分布式系统
目录 基于Node.js的分布式应用程序架构设计与最佳实践:实现高效.可扩展的分布式系统 随着互联网的普及和发展,分布式系统已经成为现代应用程序中不可或缺的一部分.而Node.js作为当前最流行的Ja ...
- 构建高可用性的 SQL Server:Docker 容器下的主从同步实现
摘要:本文将介绍如何在 Docker 环境下搭建 MS SQL Server 的主从同步,帮助读者了解主从同步的原理和实现方式,进而提高数据的可靠性和稳定性. 一.前言 在当今信息化的时代,数据的安全 ...
- Linux内核笔记(三)内核编程语言和环境
学习概要: Linux内核使用的编程语言.目标文件格式.编译环境.内联汇编.语句表达式.寄存器变量.内联函数 c和汇编函数之间的相互调用机制Makefile文件的使用方法. as86汇编语言语法 汇编 ...
- Spring-Bean(三)
Bean生命周期配置 init-method:指定类中的初始化方法名称 destory-method:指定类中销毁方法名称 Bean标签配置 <bean id="UserDao&quo ...
- k8s+containerd安装
准备环境 准备两台服务器节点,如果需要安装虚拟机,可以参考<wmware和centos安装过程> 机器名 IP 角色 CPU 内存 centos01 192.168.109.130 mas ...
- 列式分布式数据库ClickHouse部署
简介 Clickhouse是俄罗斯yandex公司2016年开源的用于OLAP的列式数据库. 使用C++语言编写,支持SQL实时查询. OLTP,更强调数据的完整性,行式存储: OLAP,更强调数据处 ...