BZOJ #2144 跳跳棋 (lca和gcd的模型转化和完美结合)

题目描述:

在数轴上有三个棋子,初始时位置为$x,y,z$,要求用最少的操作步数让位置变成$a,b,c$。每次操作可以任选一颗棋子,以另一个棋子为中轴跳动,跳动后距离不边,且每次只能跳过一颗棋子。

解题思路:

因为有限制,两边只有一颗棋子能跨过中间跳,而中间的棋子就可以往两边跳。如果注意观察的话,会发现往两边跳和往中间跳是互逆的。所以我们把状态连接起来是一棵树,而题目要求的就是始末状态在树上的距离。但如果每次暴力往上跳的话,时间复杂度会爆炸。再仔细观察的话,还会发现设中间棋子与两边棋子的距离为$(l,r)$的话,假设$l>r$,那么每次往上跳就会变成$(l-r,r)$,而这与求$gcd$的过程类似,所以我们就可以$log$地求出一个点的祖先。外面二分深度,就能求出$lca$。

代码:

 1 #include <cstdio>
 2 #include <cstring>
 3 #include <algorithm>
 4 using namespace std;
 5 
 6 const int inf = 1e9;
 7 int x, y, z, len1, len2, len;
 8 
 9 struct sta {
10     int x, y, z;
11     sta(int _x = 0, int _y = 0, int _z = 0) {x = _x, y = _y, z = _z;}
12     void sort() {
13         if (y < x) swap(x, y);
14         if (z < x) swap(x, z);
15         if (z < y) swap(z, y);
16     }
17 } st, en, s, e;
18 
19 int equal(sta a, sta b) {
20     if (a.x != b.x || a.y != b.y || a.z != b.z) return 0;
21     return 1;
22 }
23 
24 sta find(sta now, int r, int &len) {
25     int k = 0;
26     for (len = 0; r; len += k) {
27         int a = now.y - now.x, b = now.z - now.y;
28         if (a == b) return now;
29         if (a > b) {
30             k = min((a - 1) / b, r);
31             r -= k;
32             now.y -= k * b, now.z -= k * b;
33         }
34         if (a < b) {
35             k = min((b - 1) / a, r);
36             r -= k;
37             now.x += k * a, now.y += k * a; 
38         }
39     }
40     return now;
41 }
42 
43 void divide() {
44     st = find(st, len1 - len2, len);
45     int l = 0, r = len2;
46     while (l < r) {
47         int m = l + r >> 1;
48         if (equal(find(st, m, len), find(en, m, len))) r = m; else l = m + 1;
49     }
50     printf("%d", l * 2 + len1 - len2);
51 }
52 
53 int main() {
54     scanf("%d %d %d", &x, &y, &z);
55     st = sta(x, y, z), st.sort();
56     scanf("%d %d %d", &x, &y, &z);
57     en = sta(x, y, z), en.sort();
58     s = find(st, inf, len1);
59     e = find(en, inf, len2);
60     if (!equal(s, e)) {
61         printf("NO\n");
62         return 0;
63     }
64     printf("YES\n");
65     if (len1 < len2) swap(st, en), swap(len1, len2);
66     divide();
67     return 0;
68 }

 

posted @ 2016-08-17 22:39  Awner  阅读(435)  评论(0编辑  收藏  举报