【NOIP2016提高A组模拟8.15】Throw

44 篇文章 0 订阅
10 篇文章 0 订阅

本来这题是弃疗的,但是有人突然A了,我又燃起了希望
这里写图片描述

Input

六个数,开始位置和目标位置

Output

如果不行,NO,否则输出YES和步数

Sample Input

1 2 3
0 3 5

Sample Output

YES
2

Solution

三个人(x,y,z)的跳来跳去可以看做是一个二元组(l,r)在不停的变,l=y-x,r=z-y
当(x,y,z)跳时变成(x+l,y+l,z)…………等等,反应到二元组就是(l,r-l)(l-r,r),那么将状态(l,r)与前面两个状态连边,就变成了树形结构,将起始点与目标点的状态求距离就是答案。
二元组的变化很像辗转相除法,那就这么求深度,然后求LCA时用倍增思想,一次跳 2k 步,即(l,r)可能变成(l-k*r,r)

Code

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,a,b) for(int i=a;i>=b;i--)
using namespace std;
int an;
int dep(int l,int r)
{
    int k=0;for(int q=l/r,e=l%r;l!=r;l=r,r=e,q=l/r,e=l%r) if(e!=0) k+=q;else {k+=q-1;l=r;break;}
    an=l;return k;
}
void jump(int &l,int &r,int k){
    int q;
    while(k>0)
    {
        if(l>r) q=min(l/r,k),l-=q*r;
        else q=min(r/l,k),r-=q*l;k-=q;
    }
}
int aj(int l,int r,int k){
    while(k>0)
    {
        if(l==0||r==0)return 0;int q;
        if(l>r) q=min(l/r,k),l-=q*r;
        else q=min(r/l,k),r-=q*l;k-=q;
    }
    if(l==0||r==0) return 0;return l;
}
void px(int &a,int &b,int &c)
{
    if(a>b)swap(a,b);if(b>c)swap(b,c);if(a>b)swap(a,b);
}
int main()
{
    int x,y,z,x1,y1,z1;
    scanf("%d%d%d%d%d%d",&x,&y,&z,&x1,&y1,&z1);
    px(x,y,z);px(x1,y1,z1);
    int l=y-x,r=z-y,l1=y1-x1,r1=z1-y1;
    int k=dep(l,r);int an1=an;int k1=dep(l1,r1);
    if(an1!=an) {printf("NO");return 0;}int ans=0;
    fd(i,log2(k),0) if(k-(1<<i)>=k1) ans+=1<<i,jump(l,r,(1<<i)),k-=(1<<i);
    fd(i,log2(k1),0) if(k1-(1<<i)>=k) ans+=1<<i,jump(l1,r1,(1<<i)),k1-=(1<<i);
    fd(i,min(log2(k),log2(k1)),0) 
    if(aj(l,r,1<<i)!=aj(l1,r1,1<<i)) ans+=(1<<i)*2,jump(l,r,(1<<i)),jump(l1,r1,(1<<i));
    if(l!=l1) ans+=2;
    printf("YES\n%d",ans);
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值