/* 返回顶部 */

Luogu P1852 BZOJ 2144 [国家集训队]跳跳棋

qwq

这题一看就不会,如果不是gg让做我是坚决不会做的

画图模拟,因为一次只能跳过一个棋子,所以对于一种情况只有三种移动方式:

  1. 中间向左跳
  2. 中间向右跳
  3. 左或右(距中间近的那个)向中间跳

发现,除了跳到边界,当左右到中间的距离相等的时候就不能再向中间跳了,

而任意一种情况只要一直重复方式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;
}
View Code

 

posted @ 2019-03-29 19:04  Mogeko  阅读(167)  评论(3编辑  收藏  举报