HDU--3830[Checkers] LCA+二分

47 篇文章 1 订阅
25 篇文章 0 订阅

今天比赛的时候没来得及看,后然赛后想想也不太会。看了别人的题解我只能说“太神奇”了、、、

 

某牛blog上讲的很详细,而且我也是按照他的思路写的。说以,直接偷懒把他的思路贴出来、、、、、、

 

---------------------------------------------------------------------------以下摘自某牛blog-------------------------------------------------------------------------------

知识点:LCA,二叉树,二分答案

首先,我们把3个数排好序。设三个数从小到大是a, b, c

设:s1=b-as2=c-b

那么b可以跳动到a左边,或者c右边。

如果s1<s2,那么a可以跳到bc中间

如果s1>s2,那么c可以跳到ab中间

也就是说,如果s1≠s2,那么一个局面有3种跳法。

如果s1=s2,那么只有2种跳法。

如果我们用图来表示状态之间的关系,就很容易发现,状态之间组成的联

系实际上是二叉树组成的森林。

每一个s1=s2的状态都是一棵二叉树的根。

其余的每个状态,a或c往中间跳表示往父亲节点走一步

对于所有状态,中间节点往左右跳分别对应往左右孩子走一步。

原问题转换成了树上最短路问题。我们设起始状态对应节点p,目标状态对

应节点q。那么问题是:1.p和q是否同根。 2.如果同根,求p到q的距离。

这两个问题都可以用LCA的知识来解决。(LCA最近公共祖先)

如果p和q不存在LCA那么输出NO。

如果存在,那么计算LCA(p,q)到p和q分别的距离,相加即为答案。

我们可以通过辗转相除法直接计算p和q在二叉树中的深度。

为了方便求LCA,我们首先把p和q深度调整到相同。h(x)表示x的深度。

不妨设h(p)≤h(q)。我们把q往上走h(q)-h(p)步。

求2个深度相同的点的LCA,我们可以采用二分答案的方法。

对于二分答案:LCA到p的距离mid,

如果p往上走mid和q往上走mid到达的点相同,

那么 答案≤mid

否则 答案>mid

如此一来,我们得到了一个O((logS)^2)的算法。问题被完美解决。


PS.以上摘自http://blog.csdn.net/wsniyufang/article/details/6605462

 

----------------------------------------------------------------------------以上摘自某牛blog-------------------------------------------------------------------------------

 

CODE:

/*LCA+二分*/
/*
问题分成两个部分:
(1):判断两个状态是否同根;
(2):如果同根计算它们的最近公共祖先
*/
/*AC代码:0ms*/
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <memory.h>
using namespace std;
struct Node
{
    __int64 x,y,z,d;//状态的三个点坐标及该状态所在的深度
};
struct Node start,end;
void Run(Node &u)
{
    __int64 num[3];
    num[0]=u.x;num[1]=u.y;num[2]=u.z;
    sort(num,num+3);
    u.x=num[0];u.y=num[1];u.z=num[2];
}
bool equal(Node u,Node v)
{
    if(u.x==v.x&&u.y==v.y&&u.z==v.z)
        return true;
    return false;
}

Node get_root(Node &u)//返回当前节点所在子树的根节点
{
    Node t=u;
    __int64 p,q,r;
	p=t.y-t.x;q=t.z-t.y;
	while(p!=q)
	{
		if(q>p)//右边大于左边 
		{
			r=(q-1)/p;
			t.x+=r*p;
			t.y+=r*p;  
		}
		else
		{
			r=(p-1)/q;
			t.z-=r*q;
			t.y-=r*q; 
		}
		u.d+=r;
		Run(t);//注意每次要维护
		p=t.y-t.x;q=t.z-t.y;
	}
	return t;
}
Node get_pre(Node &u,__int64 step)//返回u节点前面step步的节点 
{
	Node t=u;
	__int64 p,q,r;
	//t.d-=step;
	while(step>0)
	{
		p=t.y-t.x;q=t.z-t.y;
		//printf("*\n");
		if(q>p)//右边大于左边 
		{
			r=(q-1)/p;
			if(r>step) r=step;
			t.x+=r*p;
			t.y+=r*p;  
		}
		else
		{
			r=(p-1)/q;
			if(r>step) r=step;
			t.z-=r*q;
			t.y-=r*q; 
		}
		step-=r;
		Run(t);//注意每次要维护
	}
	return t;
}
int main()
{
    int a,b,c,x,y,z;
	__int64 l,r,mid;
    while(scanf("%d%d%d%d%d%d",&a,&b,&c,&x,&y,&z)!=EOF)
    {
        start.x=a;start.y=b;start.z=c;start.d=0;
        end.x=x;end.y=y;end.z=z;end.d=0;

        Run(start);
        Run(end);
		if(!equal(get_root(start),get_root(end)))
		{
			printf("NO\n"); 
		}
		else
		{
			//化成同一层
			__int64 len=start.d>end.d?start.d-end.d:end.d-start.d;
			if(start.d>end.d)
				start=get_pre(start,len);
			else
				end=get_pre(end,len);
			//二分
			l=0;r=start.d<end.d?start.d:end.d;
			while(l!=r)
			{
				
				mid=(l+r)>>1;
				if(equal(get_pre(start,mid),get_pre(end,mid)))
					r=mid;
				else
					l=mid+1; 
			} 
			printf("YES\n");
			printf("%I64d\n",2*l+len);
		} 
    }
return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

__简言

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值