P1852 跳跳棋 题解

建模神题!!!

Solution

不妨设一个单调增的三元组 \((a,b,c)\)
你发现这个三元组的范围,是可以无限扩大的,令人无从下手!
具体的:

  • \((a,b,c) \to (a,c,2b-c) \to ....\)
  • \((a,b,c) \to (2a-b,a,c) \to ....\)

但是!这个三元组的范围却可以有限次缩小!

\(d_{1} = b - a,d_{2} = c - b\)

分类讨论有:

  • 情况 A : \(d_{1} > d_{2},(a,b,c) \to (a,2b-c,b)\)
    • 此时 \(d_{1} -= d_{2},d_{2} = d_{2}\)
  • 情况 B : \(d_{1} < d_{2}, (a,b,c) \to (b,2b-a,c)\)
    • 此时 \(d_{2} -= d_{1},d_{1} = d_{1}\)
  • 情况 C : \(d_{1} = d_{2}, (a,b,c) \to (a,b,c)\)
    • 此时 \(d_{1} = d_{1},d_{2} = d_{2}\)

不难发现情况 C 是永远不能再缩小了,所以C是一个状态的终点,但同时你可以从这个终点出发扩大出无数种状态,但这些状态最后必定殊途同归,可以回到这个终点。

殊途同归也就意味着有共同的祖先?!

所以如果以 C 状态为根节点,它可以扩展出一个无限大的搜索树,而对于题目给出的两种状态,如果能互相到达,必定都在这一颗树上!也就是说我们要求这两个状态的树上距离!

首先考虑找到根节点跳跃的过程:

观察情况 A,B 对于 \(d_{1},d_2\) 的操作,还有 \(d_3\) 的终点,是不是和更相减损术很像,如果你再手画一下,不难发现情况 A,B 都可以加速到一次跳 $\left \lfloor \frac{\max{(d_1,d_2)} - 1}{\min{(d_1,d_2)}}\right \rfloor $ 次,上面的 \(\max\) 减一是防止最后出现距离相等的情况。那这实际上就是取模的过程了,复杂度成功变成了 \(\gcd\)\(\log\) 级别!

那么再考虑倍增跳 \(lca\) 的过程:

  • 先将两个结点跳到同一深度。
  • 两个节点一起往上跳。

所以这里我们设计一个函数 \(f(x,y)\) 表示状态 \(x\) 向上跳 \(y\) 步得到的状态,且最多跳到根节点。

首先我们可以得到两个状态的根,和步数。

如果根状态不同,则无解。

否则先让深的向上跳,使得深度相同。

那到这里就很好做了,答案显然是让 \(y\) 最小且刚好到根,如果根据能否跳到根这一条件,那么对于 \(y\) ,是有 \(0\to1\) 的单调性的,所以用二分答案来找这个最小值即可。

#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=(a);i<=(b);++i)
#define Rep(i,a,b) for(int i=(a);i<(b);++i)
#define rrep(i,a,b) for(int i=(a);i>=(b);--i)
using namespace std;
template <typename T>
inline void read(T &x){
	x=0;char ch=getchar();bool f=0;
	while(ch<'0'||ch>'9'){if(ch=='-')f=1;ch=getchar();}
	while(ch>='0'&&ch<='9')x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
	if(f)x=-x;
}
template <typename T,typename ...Args>
inline void read(T &tmp,Args &...tmps){read(tmp);read(tmps...);}
struct qwq{
	int a[3];
	inline void init(){
		rep(i,0,2)read(a[i]);
		sort(a,a+3);
	}
}a,b,aa,bb;
bool operator ==(qwq x,qwq y){rep(i,0,2)if(x.a[i] != y.a[i])return 0;return 1;}
bool operator !=(qwq x,qwq y){rep(i,0,2)if(x.a[i] != y.a[i])return 1;return 0;}
int s;
qwq get(qwq x,int y){
	int z;
	for(s = 0;y;s += z){
		int d1 = x.a[1] - x.a[0],d2 = x.a[2] - x.a[1];
		if(d1 == d2)return x;
		if(d1 < d2){
			z = min(y,(d2 - 1) / d1);
			x.a[0] += z * d1;
			x.a[1] += z * d1;
		}
		else{
			z = min(y,(d1 - 1) / d2);
			x.a[2] -= z * d2;
			x.a[1] -= z * d2;
		}
		y -= z;
	}
	return x;
}
signed main(){
	a.init();b.init();
	aa = get(a,2e9);
	int s1 = s;
	bb = get(b,2e9);
	int s2 = s;
	if(aa != bb)return puts("NO"),0;
	puts("YES");
	if(s1 < s2){
		swap(a,b);swap(s1,s2);
	}
	a = get(a,s1-s2);
	int l = 0,r = s2;
	while(l < r){
		int mid = (l + r) >> 1;
		if(get(a,mid) == get(b,mid))r = mid;
		else l = mid + 1;
	}
	printf("%d",l * 2 + s1 - s2);
}
posted @ 2022-09-05 08:44  Xu_brezza  阅读(44)  评论(1编辑  收藏  举报