bzoj 2144: 跳跳棋

来源:互联网 发布:淘宝详情页价格 编辑:程序博客网 时间:2024/05/01 21:40

顶级大神题QAQ
完全没思路了这次。。

然后就去膜了题解。。感觉我很诚实啊
感觉真是神奇啊。。

我们先把棋子排序,这样好搞一点
首先,题目有要求,不可以跳过别人
那么这也就是说,两边的棋子只有一个是可以动的,但是中间向左向右跳都可以
当然,也有左右都不能动的情况
所以一个点就有三种扩展方式啦!
然后动的方式是可逆的,这个没有问题。。
于是问题可以想象为一棵树,向上跳是两旁的跳,然后儿子就是中间的跳
于是问题就转化为求树上两个点的距离
但是这题不是一颗树,是一个森林。。这个大家都知道吧,所有有NO的情况。。
然后求他的父亲的时候,要一次跳多个,要不太慢

就这样吧

#include<cstdio>#include<algorithm>#include<iostream>#include<cstring>using namespace std;const int MAX=1<<30;struct qq{    int x[5];    void read () {for (int u=1;u<=3;u++) scanf("%d",&x[u]);}    void Sort () {sort(x+1,x+4);}    bool operator != (const qq &a)const    {return x[1]!=a.x[1]||x[2]!=a.x[2]||x[3]!=a.x[3];};}S,T;int mymin (int x,int y){return x<y?x:y;}qq cal (qq a,int k,int &ret){    qq ans=a;    int dis1=a.x[2]-a.x[1],dis2=a.x[3]-a.x[2];/*  for (int u=1;u<=3;u++) printf("%d ",a.x[u]);    printf("\n");*/    if (dis1==dis2)//到这一棵树的根了        return ans;    else if (dis1<dis2)//左边跳    {        int t=mymin(k,(dis2-1)/dis1);// -1 是避免跳重合了         k-=t;        ret+=t;        ans.x[2]+=t*dis1;        ans.x[1]+=t*dis1;    }    else    {        int t=mymin(k,(dis1-1)/dis2);        k-=t;        ret+=t;        ans.x[3]-=t*dis2;        ans.x[2]-=t*dis2;    }    if (k!=0) return cal(ans,k,ret);    return ans;}int main(){    S.read();S.Sort();    T.read();T.Sort();    int lalal1=0,lalal2=0;    qq a=cal(S,MAX,lalal1),b=cal(T,MAX,lalal2);//这两个节点的深度 //  printf("%d %d\n",lalal1,lalal2);    if (a!=b) {printf("NO");return 0;}//这两个点不在一棵树上,不可能有解    else    {        printf("YES\n");        if (lalal1>lalal2)        {            swap(lalal1,lalal2);            swap(S,T);        }//让T更深        int ans=lalal2-lalal1;//先跳到同一深度        int A;        T=cal(T,ans,A);        int l=0,r=lalal1,ans1;        while (l<=r)        {            int mid=(l+r)>>1;            if (cal(S,mid,A)!=cal(T,mid,A)) l=mid+1;            else {r=mid-1;ans1=mid;}         }          printf("%d\n",ans+ans1*2);    }     return 0;}
原创粉丝点击