HDU-3830-Checkers(思维,转化成LCA+二分)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3830

题目大意:给出两组数。每个数都在一个数轴上的对应位置,一个数可以从一个数上跳到对称的位置,但是不能飞过两个点,即:

如图所示:A点可以跳到A',但C不能越过B和A。

问两组数可不可以最终都在一个位置,输出最小的跳跃次数。(想要移动的话只能从一个棋子上面跳,不能自己走)

比如样例:1,2,3和0,3,5,:

先是2经过1变成了0,然后1经过3变成了5,最后成了0,3,5;跳2步。无法得出答案输出NO。

思路:

经过读题我们发现,因为只能跳着走。然后画一画样例,我们发现只要一直向里折叠,最后得到的不能折叠的点就是根了,其他的所有情况都是从根处展开的。那么我们可以首先把每个点都折叠到最小,判断是不是一样的即可得出YES||NO。因为题目限制,所以这个是可行的,至于证明?没有,反正我就这样过的。

接下来就找最小的折叠次数了。

等等,突然想到了一组样例,比如0 1 1000000000 这组数据如何折叠到最后?遍历肯定不行吧,我们试着找一下规律:

0 1 1000000000

   1 2 1000000000

      2 3 100000000

         3 4 100000000...........999999998 999999999 1000000000

咦?发现了吧!我们只用找到len1=b-a,len2=c-b,然后len2/len1就是我们跳的次数了!这样明显快多了。

好了,剩下的就是求a,b,c和x,y,z最近的相交的地方了。因为我们每次都是向内折叠,保证到达一个点是最快的,因此我们只用找LCA(Node(a,b,c),Node(x,y,z))就好了。至于如何找,我们首先将他们的深度到同一层。然后对LCA进行二分。找最小的ans满足祖先相等即可。

ACCode:

//#pragma comment(linker, "/STACK:1024000000,1024000000")
  
#include<stdio.h>
#include<string.h> 
#include<math.h> 
   
#include<map>  
#include<set>
#include<deque> 
#include<queue> 
#include<stack> 
#include<bitset>
#include<string> 
#include<fstream>
#include<iostream> 
#include<algorithm> 
using namespace std; 
  
#define ll long long 
#define Pair pair<int,int>
//#define max(a,b) (a)>(b)?(a):(b)
//#define min(a,b) (a)<(b)?(a):(b)
#define clean(a,b) memset(a,b,sizeof(a))// ??
//std::ios::sync_with_stdio(false);
//  register
const int MAXN=1e5+10;
const int INF32=0x3f3f3f3f;
const ll INF64=0x3f3f3f3f3f3f3f3f;
const ll mod=1e9+7;
const double PI=acos(-1.0);
const double EPS=1.0e-8;

struct Node1{
	ll a,b,c,dep;
	Node1(ll _a=0,ll _b=0,ll _c=0,ll _dep=0){
		a=_a;b=_b;c=_c;dep=_dep;
	}
	friend int operator == (const Node1 &a,const Node1 &b){
		return a.a==b.a&&a.b==b.b&&a.c==b.c;
	}
};
Node1 GetRt(ll a,ll b,ll c){
	ll dep=0;
	while(1){
//		cout<<a<<" "<<b<<" "<<c<<endl;
//		ll a1=1ll*2*b-a,c1=1ll*2*b-c;
		ll len1=b-a,len2=c-b;
		if(len1==len2) break;
		else if(len1>len2){//c向b折 
			ll temp=len1/len2;
			if(len1%len2==0) temp--;
			dep+=temp;
			b-=temp*len2;
			c-=temp*len2;
		}
		else{//a向b折 
			ll temp=len2/len1;
			if(len2%len1==0) temp--;
			dep+=temp;
			a+=temp*len1;
			b+=temp*len1;
		}
	}return Node1(a,b,c,dep);
}
Node1 Move(Node1 x,ll mid){
	ll a=x.a,b=x.b,c=x.c;
	ll dep=0;
	while(1){
//		cout<<"dep,mid: "<<dep<<" "<<mid<<endl;
		if(dep==mid) break;
		ll len1=b-a,len2=c-b;
//		if(len1==len2)
		if(len1>len2){
			ll temp=len1/len2;
			if(len1%len2==0) temp--;
			if(temp+dep>mid) temp=mid-dep;
			dep+=temp;
			c-=temp*len2;
			b-=temp*len2;
		}
		else{
			ll temp=len2/len1;
			if(len2%len1==0) temp--;
			if(temp+dep>mid) temp=mid-dep;
			dep+=temp;
			a+=temp*len1;
			b+=temp*len1;
		}
	}return Node1(a,b,c,dep);
}
//int Judge(Node1 a,Node1 b,ll mid){
//	Node1 rt1=Move(a,mid),rt2=Move(b,mid);
//	return rt1==rt2;
//}
int main(){
	ll a,b,c,x,y,z;
	while(~scanf("%lld%lld%lld%lld%lld%lld",&a,&b,&c,&x,&y,&z)){
		ll tempa[3]={a,b,c},tempx[3]={x,y,z};
		sort(tempa,tempa+3);sort(tempx,tempx+3);
		Node1 rt1=GetRt(tempa[0],tempa[1],tempa[2]);
		Node1 rt2=GetRt(tempx[0],tempx[1],tempx[2]);
//		printf("%lld %lld %lld\n",rt1.a,rt1.b,rt1.c);
//		printf("%lld %lld %lld\n",rt2.a,rt2.b,rt2.c);
		if(rt1==rt2) printf("YES\n");
		else{
			printf("NO\n");
			continue;
		}
		Node1 A=Node1(tempa[0],tempa[1],tempa[2],rt1.dep);
		Node1 B=Node1(tempx[0],tempx[1],tempx[2],rt2.dep);
		if(A.dep<B.dep) swap(A,B);
		ll ans=A.dep-B.dep;
		A=Move(A,ans);A.dep=B.dep;
		ll l=0,r=A.dep,mid;
//		cout<<"l,r: "<<l<<" "<<r<<endl;
		while(l<=r){
			mid=(l+r)>>1;
//			cout<<l<<" "<<r<<endl;
			if(Move(A,mid)==Move(B,mid)) r=mid-1;
			else l=mid+1;
		}ans+=1ll*2*l;
		printf("%lld\n",ans);
	}
	
}

/*

0 1 1000000000
2 3 1000000000
*/

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值