• BZOJ2144 跳跳棋


    题目蓝链

    Solution

    这道题有一个很巧妙的性质,那就是所有的状态都是由三个两两差距相等的点转移过来的。所以我们一开始只需要把两边的点往中间转移,如果最后转移到一个状态,那么就有解

    现在我们考虑怎么快速转移。我们设当前的两两差距为((a, b)),我们有两种转移方法,((a - b, b))((a, b - a))。其实这个转移等价于((a \% b, b))((a, b \% a)),每次取模会至少把数字减少一半,所以这个过程的复杂度是(log)级别的。

    现在我们可以利用这个方法快速求出任意一个状态往中间转移(k)次后的状态,然后我们就可以利用一个类似于在树上找LCA的方法求出他们最近的公共的状态就可以了(其实感觉是二分),答案就是起始状态与目标状态到这个公共状态的距离和

    Code

    #include <bits/stdc++.h>
    
    using namespace std;
    
    #define squ(x) ((LL)(x) * (x))
    #define debug(...) fprintf(stderr, __VA_ARGS__)
    
    typedef long long LL;
    typedef pair<int, int> pii;
    
    inline int read() {
    	int sum = 0, fg = 1; char c = getchar();
    	for (; !isdigit(c); c = getchar()) if (c == '-') fg = -1;
    	for (; isdigit(c); c = getchar()) sum = (sum << 3) + (sum << 1) + (c ^ 0x30);
    	return fg * sum;
    }
    
    const int inf = 1e9;
    
    struct node {
    	int x, y, z;
    	bool operator == (const node &t) { return (x == t.x) & (y == t.y) & (z == t.z); }
    	bool operator != (const node &t) { return (x != t.x) | (y != t.y) | (z != t.z); }
    	void input() {
    		static int tmp[3];
    		for (int i = 0; i < 3; i++) tmp[i] = read();
    		sort(tmp, tmp + 3);
    		x = tmp[0], y = tmp[1], z = tmp[2];
    	}
    }a, b;
    
    node get_fa(node now, int res, int &hk) {
    	int k;
    	for (hk = 0; res; hk += k) {
    		int x = now.y - now.x, y = now.z - now.y;
    		if (x == y) break;
    		if (x < y) {
    			k = min((y - 1) / x, res);
    			now.x += k * x; now.y += k * x;
    			res -= k;
    		}else {
    			k = min((x - 1) / y, res);
    			now.y -= k * y; now.z -= k * y;
    			res -= k;
    		}
    	}
    	return now;
    }
    
    int main() {
    #ifdef xunzhen
    	freopen("jump.in", "r", stdin);
    	freopen("jump.out", "w", stdout);
    #endif
    
    	a.input(), b.input();
    	int da, db;
    	if (get_fa(a, inf, da) != get_fa(b, inf, db)) {
    		printf("NO
    ");
    		return 0;
    	}
    	if (da < db) swap(da, db), swap(a, b);
    
    	int hk;
    	a = get_fa(a, da - db, hk);
    
    	int l = 0, r = inf;
    	while (l <= r) {
    		int mid = (l + r) >> 1;
    		if (get_fa(a, mid, hk) == get_fa(b, mid, hk)) r = mid - 1;
    		else l = mid + 1;
    	}
    	r++;
    	printf("YES
    %d
    ", (r << 1) + (da - db));
    
    	return 0;
    }
    

    Summary

    这真的是一道神题,感觉需要一定的做题经验才能发现这个题的神奇性质

    以后看到这种多次相同的两个数相减,都一定要往取模的方向去想

  • 相关阅读:
    dnn重置Host密码
    fiddle 网址过滤
    生成下面的模块时,启用了优化或没有调试信息
    关闭web.config的继承
    jQuery最佳实践
    坐标高速插入,移动和查询算法
    索引缓存方面的一些测试数据
    转帖微軟将从 .NET 4 以后的版本弃用 System.Data.OracleClient
    算法系列计数排序
    一款免费生成流程图的插件
  • 原文地址:https://www.cnblogs.com/xunzhen/p/9683360.html
Copyright © 2020-2023  润新知