zoukankan      html  css  js  c++  java
  • 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
    ");
        else {
            printf("NO
    ");
            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
  • 相关阅读:
    页面抛出的"Unable to validate data"错误
    oracle的sqlnet.ora,tnsnames.ora,listener.ora三个配置文件 water
    Win7下完全卸载Oracle 11g water
    抛出多个异常 water
    【转】有关Oracle随机字符串的生成方法及具体应用 water
    【转】oracle 中随机取一条记录的两种方法 water
    【转】Oracle索引列NULL值引发执行计划该表的测试示例 water
    Oracle 取随机数 water
    dbms_output.put_line长度限制问题 water
    oracle 监听启动、停止、查看命令 water
  • 原文地址:https://www.cnblogs.com/mogeko/p/10622894.html
Copyright © 2011-2022 走看看