bzoj 2144 二分&lca 神题详解

来源:互联网 发布:金融学中的优化方法 编辑:程序博客网 时间:2024/05/22 13:36

题目:跳跳棋是在一条数轴上进行的。棋子只能摆在整点上。每个点不能摆超过一个棋子。我们用跳跳棋来做一个简单的游戏:棋盘上有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



0 0
原创粉丝点击