【國家集訓隊】跳跳棋(LCA+二分答案)

這是一道神題,不看題解永遠我永遠也不會和LCA扯上關係。不知道爲什麼會出現在“NOIP 500+”這樣的書裏...

首先,對於遞增的三個座標(x,y,z),有三種走法:

1,y向x方向跳,得到(2x-y,x,z)可以發現這是擴大了邊界。

2,y向z方向跳,得到(x,z,2z-y)可以發現這是擴大了邊界。

3,設d1=y-x, d2=z-y, 如果d1>d2,z可以跳到x,y中間,得到(x,2y-z,y);或者d2>d1,x可以跳到y,z中間得到(y,2y-x,z);由於每個座標只能放一個棋子,所以當d1=d2時不能往中間走。可以發現這是縮小了邊界。

這不就是一棵樹嗎?前兩種爲左右兒子,第三種爲父親。

進一步思考,按照前兩種走法,可以走無限步,且數字越來越大,不可取。按第三種走法,步數有限且數字減小,而且走法唯一,那麼我們就把給出的初始狀態與目標狀態同時按第三種走法走到不能再走爲止,看得到的是不是相同,若不相同則輸出NO。也就是在樹上看根是否相同。

如果可行,說明兩個節點在同一棵樹上,那麼一般只需求出LCA即可。但這題狀態太多,無法存儲。

於是想到先把兩個節點跳到同一深度 ,再二分兩個節點到LCA的距離。

但還有個貫穿始終的問題:上跳時,第3種方法一次一次跳會超時。以d1<d2爲例,直接跳到d1=d2或者d1>d2爲止,如圖:

直接跳了d2/d1步,有點類似歐幾里得求gcd。此時d1不變,d2變成d2%d1。接下來z又向v方向跳即可,依此類推,直到不能再跳。

詳見代碼。

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;

LL get(LL a,LL b,LL c,LL &dep,LL &len)
{
	LL d1=b-a,d2=c-b;
	while(d1!=d2)
	{
		if(d1<d2)
		{
			LL step=d2/d1, rest=d2%d1;
			if(rest==0)
			{
				dep+=step-1;
				a+=d1*(step-1);
				len=d1; return a;
			}
			d2=rest; a+=d1*step; b+=d1*step; dep+=step;
		}
		else
		{
			LL step=d1/d2, rest=d1%d2;
			if(rest==0)
			{
				dep+=step-1;
				len=d2; return a;
			}
			d1=rest; c-=d2*step; b-=d2*step; dep+=step;
		}
	}
	dep=0; len=d1; return a;
}

void getfa(LL &a,LL &b,LL &c,LL mid)
{
	LL d1=b-a,d2=c-b;
	while(mid)
	{
		if(d1<d2)
		{
			LL step=d2/d1, rest=d2%d1;
			if(step>=mid)
			{
				a+=d1*mid; b+=d1*mid;
				if(b==c)
				{
					b=a;
					a=b-d1;
				}
				return;
			}
			mid-=step; d2=rest; b=c-rest; a=b-d1;
		}
		else
		{
			LL step=d1/d2, rest=d1%d2;
			if(step>=mid)
			{
				c-=d2*mid; b-=d2*mid;
				if(b==a)
				{
					b=c;
					c=b+d2;
				}
				return;
			}
			mid-=step; d1=rest; b=a+rest; c=b+d2;
		}
	}
}

int main()
{
	LL ori[3],goal[3],dep1=0,dep2=0,len1,len2;
	cin>>ori[0]>>ori[1]>>ori[2];
	cin>>goal[0]>>goal[1]>>goal[2];
	sort(ori,ori+3); sort(goal,goal+3);
	
	LL a1=get(ori[0],ori[1],ori[2],dep1,len1);
	LL a2=get(goal[0],goal[1],goal[2],dep2,len2);
	
	if(a1!=a2||len1!=len2)
	{
		cout<<"NO";
		return 0;
	}
	
	cout<<"YES"<<'\n';
	
	LL cnt=0;
	if(dep1>dep2)
	{
		cnt=dep1-dep2;
		getfa(ori[0],ori[1],ori[2],dep1-dep2);
	}
	
	else if(dep2>dep1)
	{
		cnt=dep2-dep1;
		getfa(goal[0],goal[1],goal[2],dep2-dep1);
	}
	
	LL L=0,R=min(dep1,dep2),mid,ans=0,a,b,c,x,y,z;
	while(R>=L)
	{
		mid=(L+R)/2;
		getfa(a=ori[0],b=ori[1],c=ori[2],mid);
		getfa(x=goal[0],y=goal[1],z=goal[2],mid);
		if(a==x&&b==y&&c==z)
		{
			ans=mid*2;
			R=mid-1;
		}
		else L=mid+1;
	}
	cout<<cnt+ans;
	return 0;
}

 

 

發佈了126 篇原創文章 · 獲贊 8 · 訪問量 2萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章