题目:跳跳棋是在一条数轴上进行的。棋子只能摆在整点上。每个点不能摆超过一个棋子。我们用跳跳棋来做一个简单的游戏:棋盘上有3颗棋子,分别在a,b,c这三个位置。我们要通过最少的跳动把他们的位置移动成x,y,z。(棋子是没有区别的)跳动的规则很简单,任意选一颗棋子,对一颗中轴棋子跳动。跳动后两颗棋子距离不变。一次只允许跳过1颗棋子。 写一个程序,首先判断是否可以完成任务。如果可以,输出最少需要的跳动次数。
一眼看上去多像usaco的抓住那只牛,然而这道题bfs只可以得20分...(我是蒟蒻我骄傲...= =)
好吧,我们来说正经的题解
对于局势(a,b,c),我们保证a<b<c,我们讨论可以到达的局势
(1)中间的b可以随意向两边跳 即 b跳到左边 ->(2a-b,a,c)或 b跳到右边 -> (a,c,2c-b)
(2)对于两边的a和c向中间跳,
①b-a = c-b a和c都不可跳
②b-a > c-b c可以往中间跳 -> (a,2b-c,b)
③b-a < c-b a可以往中间跳 -> (b,2b-a,c)
显然,对于每种局势(a,b,c)都对应两种向两边跳的局势,而最多有一种向中间跳的;同时,相对于向两边跳的,原局势即为它们往中间跳的唯一对应的局势
所以,我们令 往中间跳可以到达的局势(a',b',c')为原局势(a,b,c)的父亲节点,向两边跳能到达的两种情况(a1,b1,c1)和 (a2,b2,c2)为 原局势(a,b,c)的子节点
那么,对于我们已知的局势(a,b,c),必然会形成一棵树,树上节点所代表的局势可互相到达
(其实这种思想和弹飞绵羊那道题很像)
如果我们的初始局势和最终局势不在同一棵树上,则必然不能完成转移,否则则可以到达
对于可以到达的情况,我们需要求出它最少的跳动次数
即求同一棵树上的两个节点间的距离,显然是lca,即求出各自到lca的距离累加即可,一般用lca或二分距离+判断即可
接下来我们需要解决的就是 如何在已知次数的情况下找到它所到达的局势 或者 在已知局势的情况的下找到转移的次数
显然后者就是我们题目所求,如果早就能解决它我们还在这里说这么一大堆做啥...
所以我们集中精力去解决第一个问题 “如何在已知次数的情况下找到它所到达的局势 ”
由于我们是求到lca的距离,所以我们只需要解决往中间跳即可
令 t1=b-a , t2=c-b
那么我们就可以用(t1,t2)来表示 局势(a,b,c)(所有的'/'表示下取整)
对于上面讨论的两边的a和c向中间跳的情况:
①t1=t2 a和c都不可跳,即当前局势为根节点
②t1>t2 c可以往中间跳 -> (t1-t2,t2),
且最多跳 (t1-1)/t2 次直到c不能跳 -> (t1 % t2,t2) ,即(a,b - (t1-1)/t2 * t2,c - (t1-1)/t2 * t2)
此时 (t1,t2)到(t1 % t2,t2)的深度(距离)为 (t1-1)/ t2
③t1<t2 a可以往中间跳 -> (t1,t2-t1),
且最多跳 (t2-1)/t1 次直到a不能跳 ->(t1,t2 % t1), 即(a + (t2-1)/t1 *t1,b + (t2-1)/t1 * t1,c)
此时 (t1,t2)到(t1,t2 % t1)的深度(距离)为 (t2-1)/ t1
类似碾转相除法的处理,我们可以在已知次数k的情况下,O(longk)找到所到达的局势
所以,对于上面所求同一棵树上两点间的距离,我们先把他们跳到同一深度,然后再二分到lca的距离,判断所到达局势是否相等即可
最终答案=abs(d2-d1)+ tt*2
(d1、d2分别代表初始局势和最终局势到根的深度,tt表示二者跳到同一高度后到lca的距离)
注意:数据保证绝对值不超多10^9,所以一开始找初始局势和自重局势各自的根节点时,步数为10^9,mdzz一直少打个0,查了一个小时的错
type
rec=array[0..3] of longint;
var
a,b,t1,t2,t :rec;
i :longint;
n,l,d1,d2,r,mid :longint;
ans,depth,tt :longint;
function min(a,b:longint):longint;
begin
if a<b then exit(a) else exit(b);
end;
procedure swap(var a,b:longint);
var
c:longint;
begin
c:=a; a:=b; b:=c;
end;
function check(a,b:rec):boolean;
var
i:longint;
begin
for i:=1 to 3 do
if a[i]<>b[i] then exit(false);
exit(true);
end;
procedure sort(var a:rec);
var
i,j:longint;
begin
for i:=1 to 3 do
for j:=i+1 to 3 do
if a[i]>a[j] then swap(a[i],a[j]);
end;
function find(a:rec;num:longint):rec;
var
t1,t2,tt:longint;
ans:rec;
begin
t1:=a[2]-a[1]; t2:=a[3]-a[2];
ans:=a;
if t1=t2 then exit(ans);
if t1<t2 then
begin
tt:=min(num,(t2-1) div t1);
dec(num,tt); inc(depth,tt);
inc(ans[2],tt*t1);
inc(ans[1],tt*t1);
end else
begin
tt:=min(num,(t1-1) div t2);
dec(num,tt); inc(depth,tt);
dec(ans[2],tt*t2);
dec(ans[3],tt*t2);
end;
if num=0 then exit(ans) else exit(find(ans,num));
end;
begin
for i:=1 to 3 do read(a[i]); sort(a);
for i:=1 to 3 do read(b[i]); sort(b);
t1:=find(a,1000000000); d1:=depth; depth:=0;
t2:=find(b,1000000000); d2:=depth; depth:=0;
if not check(t1,t2) then
begin
writeln('NO');exit;
end else
begin
writeln('YES');
if d1>d2 then
begin
swap(d1,d2);
for i:=1 to 3 do swap(a[i],b[i]);
end;
ans:=d2-d1;
t:=find(b,ans); b:=t;
l:=0; r:=d1; mid:=0;
while (l<=r) do
begin
mid:=(l+r)>>1;
t1:=find(a,mid); depth:=0;
t2:=find(b,mid); depth:=0;
if check(t1,t2) then
begin
tt:=mid; r:=mid-1;
end else l:=mid+1;
end;
inc(ans,tt*2);
writeln(ans);
end;
end.
——by Eirlys