【BZOJ】P2144 跳跳棋

LCA+二分

看了题面,再看标签:二分也就算了,但。。。LCA??
没错,就是LCA!
来慢慢分析一波。。。。。

由于一次只允许跳过1颗棋子并且两个棋子不能同时在一个点,我们以(1,2,3)为例((x,y,z)表示3个棋子分别在x,y和z位置)模拟一下.

注意红体字部分。
不难发现,当中间数向两边跳时,会产生两种未出现过的子状态,而左右两边数只能取一个距离离中间数小的向中间跳,这样产生的子状态是上一层状态

看到这里,我们假设把当前状态连条边到子状态,那么就有了

WOC!这不就是一棵树吗?
对吗?完全正确!!

那么最小变化次数不就是求树上两点之间的路径吗?
这不禁又让我们想起。。。LCA
没错,\(dis_{(x,y)}=deep_x+deep_y-2×deep_{LCA(x,y)}\)

这样我们就有大致的算法框架了。
先求出两个状态的根节点,判断是否相同,若不相同,则直接输出"NO"。
而根节点,其3个数必定构成等差数列,那么我们就存一个公差d和第一个数x,就可以确定了。

但注意一点:
若x,y,z为0,1,10^9,那么我们查找根节点时显然会TLE。
如何优化?
对于上面那组样例,我们可以想成这样

由于这么多次都是z点和x,y之间的距离都不变,也就是说x,y沿着坐标轴平移了一段距离,那么我们就可以快速地算出增加的深度和x,y的最终位置,时间就会大大降低。

至于最后计算路径长度,同样我们可以二分LCA的深度,找到LCA。(类似于倍增算法,若这两个点向上跳跃深度为x时,相等了,就往小的距离去找)

代码:

#include<bits/stdc++.h>
#define int long long
using namespace std;
int st[4],ed[4],dst,ded,lst,led,rtst,rted,ans,last;
void Get(int x,int y,int z,int &deep,int &l,int rt) {
     
    deep=0;
    int d1=y-x,d2=z-y;
    while(d1!=d2) {
        d1=y-x,d2=z-y;
        if(d1<d2) {
            int a1=d2/d1,a2=d2%d1;
            if(a2==0) {
                a1--;
                deep+=a1;
                x+=a1*d1,y+=a1*d1;
                l=d1;
                rt=x;
                return;
            } else {
                deep+=a1;
                x+=a1*d1,y+=a1*d1;
                l=d1;
            }
        } else {
            int a1=d1/d2,a2=d1%d2;
            if(a2==0) {
                a1--;
                deep+=a1;
                z-=a1*d2,y-=a1*d2;
                l=d2;
                rt=x;
                return;
            } else {
                deep+=a1;
                z-=a1*d2,y-=a1*d2;
                l=d2;
            }
        }
    }
    l=d1,rt=x;
    return;
}
void Up(int &x,int &y,int &z,int dep){
    while(dep){
        int d1=y-x,d2=z-y;
        if(d1<d2){
            int a1=d2/d1,a2=d2%d1;
            if(dep<=a1){
                x+=dep*d1,y+=dep*d1;
                return;
            }
            x+=a1*d1,y+=a1*d1;
            dep-=a1;
        }
        else {
            int a1=d1/d2,a2=d1%d2;
            if(dep<=a1){
                y-=dep*d2,z-=dep*d2;
                return;
            }
            z-=a1*d2,y-=a1*d2;
            dep-=a1;
        }
    }
}
signed main() {
    scanf("%lld %lld %lld %lld %lld %lld",&st[1],&st[2],&st[3],&ed[1],&ed[2],&ed[3]);
    sort(st+1,st+4),sort(ed+1,ed+4);
    Get(st[1],st[2],st[3],dst,lst,rtst);
    Get(ed[1],ed[2],ed[3],ded,led,rted);
    //cout<<dst<<" "<<ded;
    if(rtst!=rted||lst!=led){
        puts("NO");
        return 0;
    }
    puts("YES");
    if(dst>ded){
        ans+=dst-ded;
        dst=ded;
        Up(st[1],st[2],st[3],ans);
    }
    else {
        ans+=ded-dst;
        ded=dst;
        Up(ed[1],ed[2],ed[3],ans);
    }
    int l=0,r=dst;
    while(l<=r){
        int mid=(l+r)>>1;
        int a1=st[1],a2=st[2],a3=st[3];
        int b1=ed[1],b2=ed[2],b3=ed[3];
        Up(a1,a2,a3,mid);
        Up(b1,b2,b3,mid);
        if(a1==b1&&a2==b2&&a3==b3){
            last=2*mid;
            r=mid-1;   
        }
        else l=mid+1;
    }
    ans+=last;
    cout<<ans;
    return 0;
}

posted @ 2019-07-14 21:30  TieT  阅读(214)  评论(1编辑  收藏  举报