【BZOJ2144】跳跳棋

跳跳棋是在一条数轴上进行的。棋子只能摆在整点上。每个点不能摆超过一个棋子。我们用跳跳棋来做一个简单的游戏:棋盘上有3颗棋子,分别在a,b,c这三个位置。我们要通过最少的跳动把他们的位置移动成x,y,z。(棋子是没有区别的)跳动的规则很简单,任意选一颗棋子,对一颗中轴棋子跳动。跳动后两颗棋子距离不变。一次只允许跳过1颗棋子。  写一个程序,首先判断是否可以完成任务。如果可以,输出最少需要的跳动次数。


【题解】

在某一种状态下只有三种跳法,中间跳左边,中间跳右边,左右(离中间更近的一个)向中间跳,以当前状态下abc为节点建树,向中间跳即为当前节点的父节点,向两边跳即为当前节点的子节点,然后就是求树上两个节点间最短路的问题

1.无解当且仅当初始/末状态对应的树的根节点所表示的状态不同

2.易知从当前节点往上的(t1-1)/t2 代父节点都表示从相同的一侧往中间跳(t1为前两点/后两点之差中的较大值,t2为较小值),通过此方法可以在logk的时间内求出当前节点的弟k代祖先,然后再用一次ST算法即可。

 *上跳时要注意在(t1-1)/t2大于当前节点的深度时的处理。


【代码】(无视int64其实根本不需要)

program chess;

type
  state = record
    a, b, c: int64;
  end;
var
  s, t: state;
  s1, t1: state;
  ans: int64;
  da, db: int64;
  i: longint;

  function geth(a: state): int64;
  var
    i1, i2: int64;
    l: int64;
  begin
    geth := 0;
    i1 := a.b - a.a;
    i2 := a.c - a.b;
    while True do
    begin
      if i1 < i2 then
      begin
        l := (i2 - 1) div i1;
        if l = 0 then
          break;
        geth := geth + l;
        a.a := a.a + ((l + 1) div 2) * 2 * i1;
        a.b := a.b + (l div 2) * 2 * i1;
        if a.a > a.b then
        begin
          a.a := a.a + a.b;
          a.b := a.a - a.b;
          a.a := a.a - a.b;
        end;
        i1 := a.b - a.a;
        i2 := a.c - a.b;
      end
      else
      begin
        l := (i1 - 1) div i2;
        if l = 0 then
          break;
        geth := geth + l;
        a.c := a.c - ((l + 1) div 2) * 2 * i2;
        a.b := a.b - (l div 2) * 2 * i2;
        if a.b > a.c then
        begin
          a.b := a.b + a.c;
          a.c := a.b - a.c;
          a.b := a.b - a.c;
        end;
        i1 := a.b - a.a;
        i2 := a.c - a.b;
      end;
    end;
  end;

  function getf(a: state; b: int64): state;
  var
    i1, i2: int64;
    l: int64;
  begin
    i1 := a.b - a.a;
    i2 := a.c - a.b;
    while True do
    begin
      if i1 < i2 then
      begin
        l := (i2 - 1) div i1;
        if l = 0 then
          break;
        if l > b then
        begin
          l := b;
          b := 0;                           //这句话不能丢..
        end
        else
          b := b - l;
        a.a := a.a + ((l + 1) div 2) * 2 * i1;
        a.b := a.b + (l div 2) * 2 * i1;
        if a.a > a.b then
        begin
          a.a := a.a + a.b;
          a.b := a.a - a.b;
          a.a := a.a - a.b;
        end;
        i1 := a.b - a.a;
        i2 := a.c - a.b;
      end
      else
      begin
        l := (i1 - 1) div i2;
        if l = 0 then
          break;
        if l > b then
        begin
          l := b;
          b := 0;
        end
        else
          b := b - l;
        a.c := a.c - ((l + 1) div 2) * 2 * i2;
        a.b := a.b - (l div 2) * 2 * i2;
        if a.b > a.c then
        begin
          a.b := a.b + a.c;
          a.c := a.b - a.c;
          a.b := a.b - a.c;
        end;
        i1 := a.b - a.a;
        i2 := a.c - a.b;
      end;
      if b = 0 then
        break;
    end;
    if b = 0 then
      getf := a
    else
    begin
      getf.a := 0;
      getf.b := 0;
      getf.c := 0;
    end;
  end;

begin
  readln(s.a, s.b, s.c);
  readln(t.a, t.b, t.c);
  ans := 0;
  if s.a > s.b then
  begin
    s.a := s.a + s.b;
    s.b := s.a - s.b;
    s.a := s.a - s.b;
  end;
  if s.b > s.c then
  begin
    s.b := s.b + s.c;
    s.c := s.b - s.c;
    s.b := s.b - s.c;
  end;
  if s.a > s.b then
  begin
    s.a := s.a + s.b;
    s.b := s.a - s.b;
    s.a := s.a - s.b;
  end;
  if t.a > t.b then
  begin
    t.a := t.a + t.b;
    t.b := t.a - t.b;
    t.a := t.a - t.b;
  end;
  if t.b > t.c then
  begin
    t.b := t.b + t.c;
    t.c := t.b - t.c;
    t.b := t.b - t.c;
  end;
  if t.a > t.b then
  begin
    t.a := t.a + t.b;
    t.b := t.a - t.b;
    t.a := t.a - t.b;
  end;
  da := geth(s);
  db := geth(t);
  s1 := getf(s, da);
  t1 := getf(t, db);
  if (s1.a <> t1.a) or (s1.b <> t1.b) or (s1.c <> t1.c) then
  begin
    writeln('NO');
    exit;
  end
  else
    writeln('YES');
  ans := ans + abs(da - db);
  if da < db then
    t := getf(t, db - da);
  if da > db then
    s := getf(s, da - db);
  if da < db then
    db := da
  else
    da := db;
  for i := 30 downto 0 do
    if 1 shl i <= da then
    begin
      s1 := getf(s, 1 shl i);
      t1 := getf(t, 1 shl i);
      if (s1.a <> t1.a) or (s1.b <> t1.b) or (s1.c <> t1.c) then
      begin
        s := s1;
        t := t1;
        ans := ans + 1 shl (i + 1);
      end;
    end;
  if (s.a <> t.a) or (s.b <> t.b) or (s.c <> t.c) then
    ans := ans + 2;
  writeln(ans);
end.


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值