2018.09.01 【国家集训队】跳跳棋 (LCA)(二分答案)

版权声明:转载请声明出处,谢谢配合。 https://blog.csdn.net/zxyoi_dreamer/article/details/82284356

传送门


解析:

隐藏了的思维好题。

由于棋子之间没有区别,所以可以干一些妙妙的事情。
考虑选择一颗棋子a以b为对称点跳跃,跳完之后a和b的距离是不变的!!!
这就是本题的第一个关键,我们设三个棋子从左到右为a,b,c。a,b,c也表示其坐标。

我们显然知道如果一直是中间两个棋子向着两边跳跃,是没有尽头的,但是如果向中间跳,一定会有一个时刻,三个棋子无法再向中间跳,此时 b a == c b ,我们称其为初始状态。这是本题的第二个关键。

由初始状态开始,我们只考虑中间棋子向两边跳,每次两种选择,并且我们不考虑两边棋子向中间跳(因为一定会回到上一个状态)。所以,由同一初始状态能够转移到的状态的集合是一定的。这是本题的第三个关键。

就像一棵二叉树,离根最近的某深度的后代总是有若干个,而离一个节点某固定深度的祖先节点只有一个。

现在是重头戏。
如果 输入状态的初始状态 和输出状态的初始状态 相同,(即它们的状态在同一棵树里面)那他们一定可以相互到达,反之一定不能。

没学过倍增求LCA的小伙伴看这里

先将起始和末状态的深度较深的一个向上跳,跳到同一深度,然后就像倍增一样求一下他们的LCA。

具体求法可以用倍增也可以用二分答案。

Tips:最后一点要注意的地方:
如何跳?当a,b很近而b,c很远的时候,老老实实地跳实在是很浪费时间。
那么跳到什么时候a,b就跳不动了,再考虑换b,c跳呢?
就是跳到 c b <= b a 的时候,直接除法取得跳多少步,取模取得最终距离就好了

扫描二维码关注公众号,回复: 3017321 查看本文章

代码:

#include<bits/stdc++.h>
using namespace std;
#define re register
#define gc getchar
#define pc putchar
#define cs const
#define st static

inline
int getint(){
    re int num=0;
    re char c=gc();
    re bool f=1;
    for(;!isdigit(c);c=gc())if(c=='-')f=0;
    for(;isdigit(c);c=gc())num=(num<<1)+(num<<3)+(c^48);
    return f?num:-num;
}

struct node{
    int a,b,c;

    pair<node,int> getend(){
        node tmp=*this;
        int ans=0;
        while(b-a!=c-b){
            if(b-a>c-b){
                int dis=c-b;
                ans+=(b-a)/dis;
                b=a+(b-a)%dis;
                if(b==a)b=a+dis,--ans;
                c=b+dis;
            }
            if(b-a<c-b){
                int dis=b-a;
                ans+=(c-b)/dis;
                b=c-(c-b)%dis;
                if(b==c)b=c-dis,--ans;
                a=b-dis;
            }
        }
        node Ans=*this;
        *this=tmp;
        return make_pair(Ans,ans);
    }

    node jump(int step){
        node tmp=*this;
        while(step){
            if(b-a>c-b){
                int dis=c-b;
                if((b-a)/dis<=step){
                    step-=(b-a)/dis;
                    b=a+(b-a)%dis;
                    if(b==a)b=a+dis,++step;
                    c=b+dis;
                }
                else{
                    b-=step*dis;
                    c-=step*dis;
                    step=0;
                }
            }
            if(b-a<c-b){
                int dis=b-a;
                if((c-b)/dis<=step){
                    step-=(c-b)/dis;
                    b=c-(c-b)%dis;
                    if(b==c)b=c-dis,++step;
                    a=b-dis;
                }
                else {
                    b+=step*dis;
                    a+=step*dis;
                    step=0;
                }
            }
        }
        node Ans=*this;
        *this=tmp;
        return Ans;
    }

    node get(){
        int tmp[3];
        tmp[0]=getint();
        tmp[1]=getint();
        tmp[2]=getint();
        sort(tmp,tmp+3);
        a=tmp[0];
        b=tmp[1];
        c=tmp[2];
    }

    friend bool operator==(cs node &s,cs node &t){
        return s.a==t.a&&s.b==t.b&&s.c==t.c;
    }

    friend bool operator!=(cs node &s,cs node &t){
        return !(s==t);
    }

};

node start,expect;
int ans;
int l,r;

int main(){
    start.get();
    expect.get();
    pair<node,int> se=start.getend();
    pair<node,int> te=expect.getend();
    if(se.first!=te.first){
        puts("NO");
        return 0;
    }
    if(se.second>te.second){
        start=start.jump(se.second-te.second);
        ans+=se.second-te.second;
        r=te.second;
    }
    else if(se.second<=te.second){
        expect=expect.jump(te.second-se.second);
        ans+=te.second-se.second;
        r=se.second;
    }
    while(l<r){
        int mid=(l+r)>>1;
        if(start.jump(mid)==expect.jump(mid))r=mid;
        else l=mid+1;
    }
    puts("YES");
    cout<<(long long)ans+l*2;
    return 0;
} 

猜你喜欢

转载自blog.csdn.net/zxyoi_dreamer/article/details/82284356