Luogu P1852 BZOJ 2144 [国家集训队]跳跳棋
这题一看就不会,如果不是gg让做我是坚决不会做的
画图模拟,因为一次只能跳过一个棋子,所以对于一种情况只有三种移动方式:
- 中间向左跳
- 中间向右跳
- 左或右(距中间近的那个)向中间跳
发现,除了跳到边界,当左右到中间的距离相等的时候就不能再向中间跳了,
而任意一种情况只要一直重复方式3就能达到这样的平衡状态,也就是说这个状态可以通过方式1、2的组合达到这种情况所有的其他状态。
把这样的平衡状态当作这种情况的父亲(根节点)。
那么,首先判断两种情况的根节点是否相同,
如果相同,说明两种情况在向根节点转移的过程中可能会出现相等的情况。
最坏的情况,就是一种情况先转移到根节点,再转移到另一种情况。
如果把转移的过程看做一个树形结构,从某个点向左、右跳是左、右儿子,向中间跳(只有一种可能)是父亲,
那么从两种情况同时向上找,就类似于一个求lca的过程!
不过这棵树的左右儿子都是无限的,没法求出所有情况,所以不需要真的求出一棵树。
只要模拟在树上求LCA的过程即可。
记录左右两点到中间的距离x,y,若x<y,则左边向中间跳,即左坐标+2x,等同于左、中两点的坐标都+x,
反之则右、中两点的坐标都-y。
转移的过程中记录转移的步数可以得到深度。
首先在找根结点的时候可以得到两种情况到根结点的深度dA,dB,
然后仿照LCA的过程,把较深的一个向上走|dA-dB|步。
这时问题来了,既然没有完整的树,也就没法通过倍增的方法向上跳。
那么就只能一步一步的试了,如果向上一步后两种情况的坐标不同就两步.....
二分答案枚举步数可以优化这个过程,如果跳mid步后相同,就r=mid,否则l=mid+1。可以看出这个过程和倍增求LCA也是很相似的。
最后答案即为两个同步跳的+深的节点自己跳的=ans*2+|dA-dB|。
但是考虑这样一种情况:左、中两点非常近,但右点非常远。这时候左中两点来回相互跳要重复很多次,
这个y-x-x-x-…一直减到x≥y为止的过程,可以化简为y-kx。
k=(y-1)/x。为什么呢?当y=x的时候,其实是不能跳的。
随便带几个数试试:
坐标为1,3,9,x=2,y=6,跳(6-1)/2=2次,坐标则为5,7,9;
坐标为1,3,10,x=2,y=7,跳(7-1)/2=3次,坐标则为7,9,10,然后再跳右边的...
不过这时要注意,如果kx>规定要跳的次数res,那么跳的次数就应该是res而不是kx。
代码如下
#include<cstdio> #include<iostream> #include<algorithm> #define MogeKo qwq using namespace std; const int INF = 2e9+10; int dA,dB,dd,ans; struct node { int a,b,c; bool operator == (const node &N) const { return a==N.a && b==N.b && c==N.c; } void input() { int p[4]; for(int i = 1; i <= 3; i++) scanf("%d",&p[i]); sort(p+1,p+4); a = p[1],b = p[2],c = p[3]; } } A,B; node getfa(node t,int res,int &dpth) { int cnt; dpth = 0; while(res) { int x = t.b-t.a; int y = t.c-t.b; if(x == y) return t; if(x < y) { cnt = min((y-1)/x,res); t.a += cnt*x; t.b += cnt*x; } else { cnt = min((x-1)/y,res); t.b -= cnt*y; t.c -= cnt*y; } res -= cnt; dpth += cnt; } return t; } int main() { A.input(),B.input(); if(getfa(A,INF,dA) == getfa(B,INF,dB)) printf("YES\n"); else { printf("NO\n"); return 0; } if(dA < dB) { swap(A,B); swap(dA,dB); } A = getfa(A,dA-dB,dd); int l = 0,r = INF; while(l < r) { int mid = (l+r)>>1; if(getfa(A,mid,dd)==getfa(B,mid,dd)) { ans = mid; r = mid; } else l = mid+1; } printf("%d",ans*2+dA-dB); return 0; }