zoukankan      html  css  js  c++  java
  • [国家集训队]跳跳棋

    首先关注到这个三元组的跳法,在中间的棋子可以往两端跳,此时相邻两个棋子之
    间的距离会增大,收敛于无穷大。

    也可能存在两边棋子往中间跳,即选两边棋子中离中间棋子近的跳,相邻两个棋子
    之间的距离会减小,但是当两边到中间距离一样时就不能继续往中间跳减小距离,
    而由中间棋子为根节点,可以发散出许多种状态

    设当根节点状态相邻两个棋子距离为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;//结束程序
    }
    
    
  • 相关阅读:
    Linux上的SQL Server的起步
    SQL Server 2016 SP1 标准版等同企业版?!
    为什么简单恢复模式模式真的是坏主意?
    在SQL Serve里停用行和页层级锁
    SQL Server里的闩锁耦合(Latch Coupling)
    我有几个NUMA节点
    SQL Server里在文件组间如何移动数据?
    对于SQL Server,我需要多少内存
    SQL Server里的文件和文件组
    重启SQL Server——总是好事?
  • 原文地址:https://www.cnblogs.com/Reanap/p/13423264.html
Copyright © 2011-2022 走看看