[国家集训队]跳跳棋

首先关注到这个三元组的跳法,在中间的棋子可以往两端跳,此时相邻两个棋子之
间的距离会增大,收敛于无穷大。

也可能存在两边棋子往中间跳,即选两边棋子中离中间棋子近的跳,相邻两个棋子
之间的距离会减小,但是当两边到中间距离一样时就不能继续往中间跳减小距离,
而由中间棋子为根节点,可以发散出许多种状态

设当根节点状态相邻两个棋子距离为k,k时,可以发散出2k,k和k,2k,又可以继续发
散,发散出2k,3k和3k,k和k,3k和3k,k......以此类推,可以发现共性,只要是在这
棵搜索树上的状态相邻两颗棋子的最大公约数都是k,由此可以轻易判断两个状态是
否在同一棵搜索树上。

此时我们已经知道了这棵搜索树的根节点和扩展方式,现在要求两个状态的LCA。
首先我们显然不能建树,空间无法承受。那么我们考虑在不建树的情况求LCA。

我们面临的第一个问题就是如何求深度,因为每个孩子节点只有一个爸爸,所以肯
定可以推出任意一个节点的深度,那么怎么求呢,根据他的扩展方式我们能得出一
个辗转相减法,但是不够快,所以我们可以显而易见地优化成辗转相除。于是我们
能在logn的时间里得出深度

第二个问题,如何求LCA,我们可以在logn的时间算出节点爬升任意高度的结果,所
以我们完全可以二分答案求解。

时间复杂度 : log2 (n) * log2 (n)

代码:


#include <cstdio>
#include <algorithm>
using namespace std;
typedef long long LL;
LL a , b , c , x , y , z;
void so(LL &a , LL &b , LL &c) {
	if(a > b) swap(a , b);
	if(a > c) swap(a , c);
	if(b > c) swap(b , c);
}
LL gcd(LL a , LL b) {
	if(!b) return a;
	return gcd(b , a % b);
}
LL dep(LL x , LL y) {//求深度
	LL ans = 0;
	while(y) {
		ans += (x / y);
		x = x % y;
		swap(x , y);
	}
	return ans;
}
void up(LL &x , LL &y , LL k) { // x,y向上爬升k步
	while(k && x != y && y && x) {
		if(x > y) {
			if(y * k < x) {
				x -= y * k;
				k = 0;
			}
			else {
				k -= x / y;
				if(x % y != 0) x %= y;
				else x = y;
			}
		}
		else {
			if(x * k < y) {
				y -= x * k;
				k = 0;
			}
			else {
				k -= y / x;
				if(y % x != 0) y %= x;
				else y = x;
			}
		}
	}
}
int main() {
	scanf("%lld %lld %lld" , &a , &b , &c);
	scanf("%lld %lld %lld" , &x , &y , &z);
	so(a , b , c);
	so(x , y , z);
	if(gcd(b - a , c - b) != gcd(y - x , z - y)) {
		printf("NO");
		return 0;
	}
	printf("YES
");
	LL lena = dep(b - a , c - b) , lenb = dep(y - x , z - y);
	LL ax = b - a , ay = c - b , bx = y - x , by = z - y , tot = 0;
	if(lena < lenb) {
		up(bx , by , lenb - lena);
		tot += (lenb - lena);
		lenb = lena;
	} 
	else if(lenb < lena) {
		up(ax , ay , lena - lenb);
		tot += (lena - lenb);
		lena = lenb;
	}//统一至统一高度
	if(ax == bx && ay == by) {
		printf("%lld" , tot);
		return 0;
	}
	LL l = 0 , r = 1e9 , ans = 0;
	while(l + 1 < r) { // 二分答案
		LL mid = (l + r) >> 1;
		LL p = ax , q = ay , s = bx , t = by;
		up(p , q , mid);
		up(s , t , mid);
		if(p == s && q == t) r = mid;
		else {
			l = mid;
			ans = mid;
		}
	}
	printf("%lld" , (ans + 1) * 2 + tot);//输出
	return 0;//结束程序
}

原文地址:https://www.cnblogs.com/Reanap/p/13423264.html