【JZOJ 4701】 Throw

37 篇文章 0 订阅
5 篇文章 0 订阅

Description

这里写图片描述
这里写图片描述

Analysis

S(x,y,z) 为数轴上由左到右依次的三个点,且l=y-x,r=z-y
根据题意,y往两边跳后可以得到 S(xl,yl,z) S(x,y+r,z+r) ,而且左右两边只有一个点能往中间跳,那么我们把 S(x,y,z) 看成后两者的父节点,这就是一个树的模型。
那么答案就是树上的起点与终点的距离。
所以我们可以暴力往上跳。但TLE
继续观察,将状态表示变成 (l,r) ,则其可转移到 (l,r+l) (l+r,r)
一般地,其可转移到 (l,r+kl) (l+kr,r)
这是不是长得很像辗转相除法里面的两个参数?
这启示我们,一次跳可以跳很多步!大概是log级别的。
通过这样跳可以算深度。
那我们像倍增算法思想一样。但是那个f数组是不能求出来的,所以枚举2^k祖先就真的跳一下看看跳到哪里。 O(log22maxlen)

Code

#include<cstdio>
#include<cmath>
#include<algorithm>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,b,a) for(int i=b;i>=a;i--)
using namespace std;
int x,y,s1,s2,s3,e1,e2,e3;
void jump(int &l,int &r,int k)
{
    while(k)
    {
        if(l==r) break;
        if(l>r)
        {
            if(k<l/r)
            {
                l=l-k*r;
                break;
            }
            k-=l/r;
            if(l%r==0)
            {
                l=r;
                break;
            }
            else l-=(l/r)*r;
        }
        else
        {
            if(k<r/l)
            {
                r=r-k*l;
                break;
            }
            k-=r/l;
            if(r%l==0)
            {
                r=l;
                break;
            }
            else r-=(r/l)*l;
        }
    }
}
int dep(int l,int r)
{
    int t,k=0;
    x=l;
    while(1)
    {
        if(l<r) swap(l,r);
        if(l%r==0)
        {
            x=r,k+=l/r-1;
            break;
        }
        t=r;
        k+=l/r,r=l%r,l=t;
    }
    return k;
}
int lca(int a,int b,int c,int d)
{
    int du=dep(a,b),dv=dep(c,d),l,r,x,y,ans=0;
    if(du<dv) swap(du,dv),swap(a,c),swap(b,d);
    fd(i,log2(du-dv),0)
    {
        l=a,r=b;
        jump(l,r,1<<i);
        int dp=dep(l,r);
        if(dp>=dv) du=dp,a=l,b=r,ans+=1<<i;
        if(dp==dv) break;
    }
    if(a==c && b==d) return ans;
    fd(i,log2(du),0)
    {
        l=a,r=b,x=c,y=d;
        jump(l,r,1<<i);
        jump(x,y,1<<i);
        if(l==x && r==y) continue;
        a=l,b=r,c=x,d=y,ans+=2*(1<<i);
    }
    return ans+2;
}
int main()
{
    scanf("%d %d %d %d %d %d",&s1,&s2,&s3,&e1,&e2,&e3);
    if(s1>s2) swap(s1,s2);if(s2>s3) swap(s2,s3);if(s1>s2) swap(s1,s2);
    if(e1>e2) swap(e1,e2);if(e2>e3) swap(e2,e3);if(e1>e2) swap(e1,e2);
    int a=s2-s1,b=s3-s2,c=e2-e1,d=e3-e2;
    dep(a,b),y=x;
    dep(c,d);
    if(x!=y)
    {
        printf("NO");
        return 0;
    }
    printf("YES\n%d",lca(a,b,c,d));
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值