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
- bzoj 2144 二分&lca 神题详解
- [BZOJ]2144: 跳跳棋 二分+LCA
- BZOJ 2144 跳跳棋(LCA+欧几里德+二分答案)
- 【LCA】bzoj 2144:跳跳棋
- BZOJ 2144 LCA 解题报告
- 【BZOJ】4326 NOIP2015 运输计划 二分+LCA+树上差分
- BZOJ 4326: NOIP2015 运输计划【LCA】【二分】【差分】
- bzoj 2144: 跳跳棋 lca+倍增
- BZOJ 3626 LCA 树链剖分
- bzoj 3626: [LNOI2014]LCA
- 【BZOJ 3626】 [LNOI2014]LCA
- [bzoj 3626] LNOI2014 LCA
- BZOJ 1787 裸LCA
- bzoj 3626 LCA
- BZOJ 3626 [LNOI2014]LCA
- BZOJ 3626 [LNOI2014]LCA
- BZOJ 3626 LCA 树链剖分
- bzoj 1787 lca
- 127.0.0.1和0.0.0.0
- ios 关于Debug,ForBeta,Release 的那些小事。
- window下ionic创建WebAPP工程
- 【鼓捣树莓派】树莓派通过I2C总线连接LCD1602
- 读取文件的两种类型
- bzoj 2144 二分&lca 神题详解
- kafka介绍总结
- ajax
- Android中双击返回键退出应用
- js数组方法总结
- unix/linux more命令的实现
- 游戏玩家的留存率统计实现
- python判断字符串(string)是否包含(contains)子字符串的方法
- Java8官方文档学习笔记