• P1852 [国家集训队]跳跳棋


    P1852 [国家集训队]跳跳棋

    lca+二分

    详细解析见题解

    对于每组跳棋,我们可以用一个三元组(x,y,z)表示

    我们发现,这个三元组的转移具有唯一性,收束性

    也就是说,把每个三元组当成点,以转移关系为边,那么可以得到一棵树

    显然最短步数==lca

    然后我们就可以愉快地跑lca了

    但是还要加优化,就是有可能出现2个靠得近的棋子,但与另一个棋子离得远的情况

    这时要跳很多次,但是可以加速,详见代码

    最后二分求lca

    code:

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<cctype>
    using namespace std;
    struct node{
        int a[3];
        bool operator == (const node &tmp) const{return a[0]==tmp.a[0]&&a[1]==tmp.a[1]&&a[2]==tmp.a[2];}
    }f,t,p1,p2;
    inline int find(node x){
        int d1=x.a[1]-x.a[0],d2=x.a[2]-x.a[1]; bool c=0;
        if(d1==d2) {p1=x; return 0;}
        if(d1<d2) swap(d1,d2),c=1;
        int cnt=d1/d2,d=d1%d2; //加速跳
        if(!d) d+=d2,--cnt;
        if(c) cnt+=find((node){x.a[2]-d-d2,x.a[2]-d,x.a[2]});
        else cnt+=find((node){x.a[0],x.a[0]+d,x.a[0]+d+d2});
        return cnt;
    }
    inline void change(node x,int step){
        int d1=x.a[1]-x.a[0],d2=x.a[2]-x.a[1]; bool c=0;
        if(d1==d2||!step) {p1=x; return ;}
        if(d1<d2) swap(d1,d2),c=1;
        int cnt=d1/d2,d=d1%d2;
        if(!d) d+=d2,--cnt;
        if(c){
            if(step>=cnt) change((node){x.a[2]-d-d2,x.a[2]-d,x.a[2]},step-cnt);
            else change((node){x.a[2]-d-d2*(cnt-step+1),x.a[2]-d-d2*(cnt-step),x.a[2]},0);
        }
        else{
            if(step>=cnt) change((node){x.a[0],x.a[0]+d,x.a[0]+d+d2},step-cnt);
            else change((node){x.a[0],x.a[0]+d+d2*(cnt-step),x.a[0]+d+d2*(cnt-step+1)},0);
        }
    }
    inline bool same(int k){
        change(f,k); node r1=p1;
        change(t,k); node r2=p1;
        return r1==r2;
    }
    int main(){
        scanf("%d%d%d%d%d%d",&f.a[0],&f.a[1],&f.a[2],&t.a[0],&t.a[1],&t.a[2]);
        sort(f.a,f.a+3); sort(t.a,t.a+3);
        int sp1=find(f); p2=p1;
        int sp2=find(t); //求相对于树根的深度
        if(!(p1==p2)) {printf("NO"); return 0;} //树根不同
        if(sp1<sp2) swap(sp1,sp2),swap(f,t);
        int ans=sp1-sp2;
        change(f,ans); f=p1; //使两点同一深度
        int l=0,r=sp2; //二分找lca
        while(l<r){
            int mid=l+((r-l)>>1);
            if(same(mid)) r=mid;
            else l=mid+1;
        }printf("YES
    %d",(l<<1)+ans);
        return 0;
    }
  • 相关阅读:
    eBay商家五大市场调研工具
    最新Android ADT, SDK, SDK_tool等官方下载说明
    Android——关于ListView控件的使用方法
    Auto CAD的相关问题·绘图单位
    淘宝数据分析之数据分析工具的三个需求层次
    电阻器阻值标识的相关知识
    Android软件开发之ListView 详解
    解决android模拟器划动手势时自动输入字母“c” 的问题
    争辩:关于输出大电流的移动电源给小电流的手机充电的问题
    关于C51的中断函数要注意的几个问题
  • 原文地址:https://www.cnblogs.com/kafuuchino/p/9706409.html
Copyright © 2020-2023  润新知