【Luogu P1852】 跳跳棋
阿新 • • 發佈:2020-08-12
題目大意:
三個棋子在 \(a,b,c\) 位置,通過題目給出的跳動規則,問能否跳到 \(x,y,z\),如果能,最少多少步?
正文:
假設三個棋子分別在 \(a,b,c\quad(a<b<c)\),跳動規則其實就三個:
-
\(b\) 向 \(a\) 跳。
-
\(b\) 向 \(c\) 跳。
-
\(a,c\) 裡 \(b\) 近的向內跳。
而第 1,2 個規則都會擴大範圍,只有第 3 個規則會縮小範圍,@ButterflyDew (uid=63727) 在題解中提到 對縮小邊界的跳法具有唯一性,也就是說將初始位置和終點位置都用第 3 規則跳,若最後產生交集(即相等)說明能夠到達。
把三個數看作一個三元數 \((a,b,c)\) 作為一棵樹的一個節點的狀態,也就是說問題就是樹上兩個不同節點的距離,像 LCA 一樣,先讓兩個節點跳到一個相同的深度,再二分距離,讓這兩個節點同時向上跳,最後就能出答案。
程式碼:
int jump(int a, int b, int c) { int d1 = b - a, d2 = c - b, cnt = 0; if (d1 < d2) { int d = d2 % d1; cnt = d2 / d1; if (!d) { cnt --; d += d1; } cnt += jump (c - d - d1, c - d, c); } else if (d1 > d2) { int d = d1 % d2; cnt = d1 / d2; if (!d) { cnt --; d += d2; } cnt += jump (a, a + d, a + d + d2); } else arr[0] = a, arr[1] = b, arr[2] = c; return cnt; } void go(int a, int b, int c, int step) { if(!step) { arr[0] = a, arr[1] = b, arr[2] = c; return; } int d1 = b - a, d2 = c - b, cnt = 0; if (d1 < d2) { int d = d2 % d1; cnt = d2 / d1; if (!d) { cnt --; d += d1; } if(step >= cnt) go (c - d - d1, c - d, c, step - cnt); else go (c - d - d1 * (cnt - step + 1), c - d - d1 * (cnt - step), c, 0); } else if (d1 > d2) { int d = d1 % d2; cnt = d1 / d2; if (!d) { cnt --; d += d2; } if(step >= cnt) go (a, a + d, a + d + d2, step - cnt); else go (a, a + d + d2 * (cnt - step), a + d + d2 * (cnt - step + 1), 0); } else arr[0] = a, arr[1] = b, arr[2] = c; } bool check(int mid) { go(beg[0], beg[1], beg[2], mid); arr1[0] = arr[0], arr1[1] = arr[1], arr1[2] = arr[2]; go(end[0], end[1], end[2], mid); if (arr1[0] != arr[0] && arr1[1] != arr[1] && arr1[2] != arr[2]) return 0; return 1; } int main() { scanf ("%d%d%d%d%d%d", &beg[0], &beg[1], &beg[2], &end[0], &end[1], &end[2]); sort (beg, beg + 3);sort (end, end + 3); int step1 = jump(beg[0], beg[1], beg[2]); arr1[0] = arr[0], arr1[1] = arr[1], arr1[2] = arr[2]; int step2 = jump(end[0], end[1], end[2]); if (arr1[0] != arr[0] && arr1[1] != arr[1] && arr1[2] != arr[2]) { puts("NO"); return 0; } if (step1 < step2) { ans = step2 - step1; go(end[0], end[1], end[2], step2 - step1); end[0] = arr[0], end[1] = arr[1], end[2] = arr[2]; } if (step1 > step2) { ans = step1 - step2; go(beg[0], beg[1], beg[2], step1 - step2); beg[0] = arr[0], beg[1] = arr[1], beg[2] = arr[2]; } int l, r = min(step1, step2); while (l < r) { int mid = (l + r) >> 1; if (check(mid)) r = mid; else l = mid + 1; } printf("YES\n%d\n", (l * 2 + ans)); return 0; }