首先关注到这个三元组的跳法,在中间的棋子可以往两端跳,此时相邻两个棋子之
间的距离会增大,收敛于无穷大。
也可能存在两边棋子往中间跳,即选两边棋子中离中间棋子近的跳,相邻两个棋子
之间的距离会减小,但是当两边到中间距离一样时就不能继续往中间跳减小距离,
而由中间棋子为根节点,可以发散出许多种状态
设当根节点状态相邻两个棋子距离为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;//结束程序
}