【BZOJ 2144】【JZOJ 4701】Throw

Description

这里写图片描述

抽象题意:有3个点在数轴上移动,每次移动可以把a点绕b转到a’,并且符合a~a’中无它点。

Solution

把3个点的位置用一个3元组来表示,(x,y,z) ,满足 x<y<z
(x,y,z)每轮的旋转有以下几种情况:
l=yxrzy
从里到外:
(x,y,z)~ (xl,yl,z)
(x,y,z)~ (x,yr,zr)
从外到里:
(x,y,z)~ (x+l,y+l,z)l<r ,
(x,y,z)~ (x,yr,zr)l>r
我们发现,这就是一棵树,
以从外到里为根,每轮不停向上跳,求LCA即可,
然而会超时 ,
把式子转化一下:
当l>r时,用可能因差距巨大,要不停的把x转过去,
然而这没有必要,因为每轮只是r=r-l,
所有我们可以一次性把r减到减不了为止,这样可以大大的减小时间,(与求gcd是类似)
分别求出目标和当前的层数后,先把目标和当前快跳到同一层,再枚举一个 2k ,看看两个点向上跳这么多后相不相同,
复杂度: O(nlog(n))

Code

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<cstring>
#include<algorithm>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fod(i,a,b) for(int i=a;i>=b;i--)
#define FZA() a1=x,a2=y,a3=z
#define FZB() b1=x,b2=y,b3=z
#define OK() (q==x&&w==y&&e==z)
using namespace std;
typedef long long LL;
const int N=100500,maxlongint=2147483640;
int m,n,ans;
int a1,a2,a3,b1,b2,b3,a0,b0;
int x,y,z,s1,s2;
int jump(int q,int w,int e)
{
    int l=w-q,r=e-w,s=0;
    x=q,y=w,z=e;
    if(r>l)
    {
        s=r/l;
        if(r%l)r=r%l;else s--,r=l;
        y=z-r;x=y-l;
        return s;
    }
    s=l/r;
    if(l%r)l=l%r;else s--,l=r;
    y=x+l;z=y+r;
    return s;
}
void jumpo(int q,int w,int e)
{
    int l=w-q,r=e-w;
    x=q,y=w,z=e;
    if(r>l)r=r-l,y=z-r,x=y-l;
        else l=l-r,y=x+l,z=y+r;
}
void jumpt(int q,int w,int e,int s1,int s2)
{
    int t;
    while(s1>s2)
    {
        t=jump(q,w,e);
        if(s1-t<s2)break;
        q=x,w=y,e=z;
        s1-=t;
    }
    x=q,y=w,z=e;
    if(s1>s2)
    {
        s1=s1-s2;
        int l=w-q,r=e-w;
        if(r>l)r=r-l*s1,y=z-r,x=y-l;
        else l=l-r*s1,y=x+l,z=y+r;
    }
}
int main()
{
    int q,w,e,t;
    scanf("%d%d%d%d%d%d",&a1,&a2,&a3,&b1,&b2,&b3);
    if(a1>a2)swap(a1,a2);if(a1>a3)swap(a1,a3);if(a2>a3)swap(a2,a3);
    if(b1>b2)swap(b1,b2);if(b1>b3)swap(b1,b3);if(b2>b3)swap(b2,b3);
    x=a1,y=a2,z=a3;
    while(y-x!=z-y)s1+=jump(x,y,z);
    q=x,w=y,e=z;
    x=b1,y=b2,z=b3;
    while(y-x!=z-y)s2+=jump(x,y,z);
    if(q!=x||w!=y||e!=z){printf("NO\n");return 0;}
    if(!s1||!s2){printf("YES\n%d\n",s1+s2);return 0;}
    ans+=abs(s1-s2);
    jumpt(a1,a2,a3,s1,s2),FZA();
    jumpt(b1,b2,b3,s2,s1),FZB();
    s1=min(s1,s2);
    t=1;
    while(t*2<=s1)t<<=1;
    while((a1!=b1||a2!=b2||a3!=b3)&&t)
    {
        while(s1<t)t/=2;
        jumpt(a1,a2,a3,s1,s1-t);
        q=x,w=y,e=z;
        jumpt(b1,b2,b3,s1,s1-t);
        if(!OK())
        {
            ans+=t*2;
            s1-=t;
            a1=q,a2=w,a3=e;
            b1=x,b2=y,b3=z;
        }
        t/=2;
    }
    while(a1!=b1||a2!=b2||a3!=b3)jumpo(a1,a2,a3),FZA(),jumpo(b1,b2,b3),FZB(),ans+=2;
    printf("YES\n%d\n",ans);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值