zoukankan      html  css  js  c++  java
  • 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;
    }
  • 相关阅读:
    包和常用内置模块(二)
    常用内置模块(一)
    正则表达式和re模块
    迭代器和生成器
    函数(四)
    函数(三)闭包函数与装饰器
    Codeforces Round #539 (Div. 2) D 思维
    Codeforces Round #539 (Div. 2) 异或 + dp
    Codeforces Round #546 (Div. 2) E 推公式 + 线段树
    牛客练习赛42 C 反着计算贡献
  • 原文地址:https://www.cnblogs.com/kafuuchino/p/9706409.html
Copyright © 2011-2022 走看看