P1852跳跳棋 题解

好妙啊!这道题让我大开眼界!
写一篇题解加深一下印象。

思路

我重点对把原问题建模成一个树上问题的部分进行补充。

最开始读题的时候没有看到最多只跳过一个棋子,没想到这竟然是突破口。

我们假设一个状态 \((x, y, z)\),不妨设 \(x \le y \le z\)

考虑它能怎么变化?这里为了方便描述,在引入两个变量 \(d1,d2\) 表示 \(x\)\(z\)\(y\) 之间的距离。

它会有以下几种变换方式:

  1. \(y\) 向左跳,\(y \to y - 2 \times d1\)\((x, y, z) \to (y - 2 \times d1, x, z)\)

  2. \(y\) 向右跳,\(y \to y + 2 \times d2\)\((x, y, z) \to (x, z, y + 2 \times d2)\)

  3. \(x\) 向右(中间)跳, \(x \to x + 2 \times d1\)\((x, y, z) \to (y, x + 2 \times d1, z)\)

  4. \(z\) 向左(中间)跳, \(z \to z - 2 \times d2\)\((x, y, z) \to (x, z - 2 \times d2)\)

可以发现由于最多只跳过一个棋子\(3\)\(4\) 操作必然矛盾。
换句话说,假如只进行 \(3\)\(4\) 操作操作将是唯一的(会操作小的那边)!(\震惊)

这里有一个小发现: \(1,2\) 操作和 \(3,4\) 操作是互逆的:
比如 \((1, 2, 9)\) 进行 \(3\) 操作 变成 \((2, 3, 9)\)\((2, 3, 9)\) 进行 \(1\) 操作 又变回 \((1, 2, 9)\)

思考继续深入。每次 \((x, y, z)\) 的范围都会减少 \(\min(d1, d2)\)

范围会在某个状态重新变大吗或无法变化吗?

答案是后者。我们刚才忘记讨论 \(d1 = d2\) 的情况了!假如 \(d1 = d2\) 那就无法操作了。

我们假设这样的状态为 \((x', y', z')\)

\((x', y', z')\)\((x, y, z)\) 要进行的操作是不确定的 (\(1\)\(2\) 其中一种)。

而从 \((x, y, z)\)\((x', y', z')\) 要进行的操作是确定的(\(3\)\(4\) 其中一种)。

并且 \(1, 2\)\(3, 4\) 操作是互逆的。

二叉树不就是这样的吗?走到儿子和回到父亲是互逆的。儿子有多个,不确定;父亲只有一个,确定。

所以从原始状态到目标状态的过程对应的就是树上两点的距离。

设原始状态,目标状态分别为 \(A\)\(B\)

解决方法就是先找到 LCA ,再计算出 \(A\)\(B\)LCA 的距离(用 \(3,4\) 操作唯一解决)

剩下部分可以参考别的题解,主要的就是我这里的 jump 函数。

#include<bits/stdc++.h>
using namespace std;
struct node{
    int x, y, z;
//	void print() {
//		cout << x << " " << y << " " << z << "\n"; 
//	}
    bool operator == (node T) {
        return (x == T.x  && y == T.y && z == T.z);
    }
}A, B;
pair<node, int> get(int x, int y, int z) {
    int res = 0;
    while(1) {
        int d1 = y - x, d2 = z - y;
        if(d1 == d2) break;
        if(d1 < d2) {
            int s = (d2 - 1) / d1;
            y = z - d2 + s * d1;
            x = y - d1;
            res += s;
        }
        else {
            int s = (d1 - 1) / d2;
            y = x + d1 - s * d2;
            z = y + d2;
            res += s;
        }		
    }
    return {(node){x, y, z}, res};
}// 跳到根的步数 

node jump(int x, int y, int z, int step) {
    while(step) {
        int d1 = y - x, d2 = z - y;
        if(d1 == d2) break;
        if(d1 < d2) {
            int s = min(step, (d2 - 1) / d1);
            y = z - d2 + s * d1;
            x = y - d1;
            step -= s;
        }
        else {
            int s = min(step, (d1 - 1) / d2);
            y = x + d1 - s * d2;
            z = y + d2;
            step -= s;
        }	
    }
    return (node){x, y, z};
}// 跳 step 步到的点 

void init(node &P) {
    int a, b, c; 
    cin >> a >> b >> c;
    P.x = min({a, b, c});
    P.z = max({a, b, c});
    P.y = a + b + c - P.x - P.z;
}

int main() {
    init(A), init(B);
    auto tmp1 = get(A.x, A.y, A.z);
    auto tmp2 = get(B.x, B.y, B.z);
    if(!(tmp1.first == tmp2.first)) {
        cout << "NO";
        return 0;
    }
    cout << "YES\n"; 
    int tot1 = tmp1.second, tot2 = tmp2.second;
    if(tot1 < tot2) swap(A, B), swap(tot1, tot2);
    A = jump(A.x, A.y, A.z, tot1 - tot2);
    int l = 0, r = 1e9, ans = 0;
    while(l <= r) {
        int mid = (l + r) / 2;
        if(jump(A.x, A.y, A.z, mid) == jump(B.x, B.y, B.z, mid)) {
            ans = mid, r = mid - 1;
        }
        else l = mid + 1;
    }
    cout << ans * 2 + tot1 - tot2;
    return 0;
}
posted @ 2025-02-20 18:49  merlinkkk  阅读(27)  评论(0)    收藏  举报