【BZOJ2144】【JZOJ4701】Throw
Description
Solution
我们设一个状态: S(a,b,c)(a<b<c) 表示三人所在位置。
设 l=b−a,r=c−b
那么如果是
b
往两边跳,那就是:
S(a,b,c)−−>S(a,b+r,c+r)
如果是两边往中间条,那就是:
l<r:S(a,b,c)−−>S(a+l,b+l,c)
l>r:S(a,b,c)−−>S(a,b−r,c−r)
把每种状态弄出来,形成了一颗树!
但是直接这样找会超时。
其实,由于可以顺操作和逆操作是一样的,那么从起始位置到目标位置的距离其实就是它们在一棵状态树上的距离。
那么就是一个求最近公共祖先的问题了。
但是,树的深度太大,不能预处理出倍增数组,这就尴尬了。
其实一点都不尴尬,我们再设一个二元组状态
S(l,r)
表示
a
,
那么一次转移:
l<r:S(l,r)−−>S(l,r−l)
l>r:S(l,r)−−>S(l−r,r)
这是辗转相减法?
于是可以用辗转相除的思想优化。
那么一次转移就变成了:
l<r:S(l,r)−−>S(l,r−kl)(k=⌊rl⌋)
(这里如果
l|r
要给
k
减一)
那么没有倍增数组怎么倍增呢?
假如我们要跳 2k 步,那就直接向上跳 2k 步,然后再判断状态的信息决定是否保留。
当然,我们还需保留三元组信息用于判断。
时间复杂度 O(log22max(|Ai|))
Code
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<algorithm>
#define fo(i,j,k) for(int i=j;i<=k;i++)
#define fd(i,j,k) for(int i=j;i>=k;i--)
#define N 10
using namespace std;
int a[N],b[N];
int c[N],d[N];
int ans=0;
void pre()
{
fo(i,1,3) c[i]=a[i];
fo(i,1,3) d[i]=b[i];
c[4]=c[2]-c[1];
c[5]=c[3]-c[2];
d[4]=d[2]-d[1];
d[5]=d[3]-d[2];
}
int turn(int x,int y)
{
if(x%y==0) return x/y-1;
else return x/y;
}
void jump(int x,bool is)
{
int p1=4,p2=5,q1=4,q2=5;
int t1=1<<x,t2=1<<x;
int l1=c[4],r1=c[5],l2=d[4],r2=d[5];
int a1=c[1],a2=c[2],a3=c[3];
int b1=d[1],b2=d[2],b3=d[3];
while(t1 || t2)
{
if(c[4]>c[5])
{
if(t1>=turn(c[4],c[5])) t1-=turn(c[4],c[5]),c[4]=c[4]-turn(c[4],c[5])*c[5];
else c[4]=c[4]-t1*c[5],t1=0;
c[2]=c[1]+c[4];
c[3]=c[2]+c[5];
}
else if(c[5]>c[4])
{
if(t1>=turn(c[5],c[4])) t1-=turn(c[5],c[4]),c[5]=c[5]-turn(c[5],c[4])*c[4];
else c[5]=c[5]-t1*c[4],t1=0;
c[2]=c[3]-c[5];
c[1]=c[2]-c[4];
}
if(d[4]>d[5])
{
if(t2>=turn(d[4],d[5])) t2-=turn(d[4],d[5]),d[4]=d[4]-turn(d[4],d[5])*d[5];
else d[4]=d[4]-t2*d[5],t2=0;
d[2]=d[1]+d[4];
d[3]=d[2]+d[5];
}
else if(d[5]>d[4])
{
if(t2>=turn(d[5],d[4])) t2-=turn(d[5],d[4]),d[5]=d[5]-turn(d[5],d[4])*d[4];
else d[5]=d[5]-t2*d[4],t2=0;
d[2]=d[3]-d[5];
d[1]=d[2]-d[4];
}
if(c[4]==c[5] && d[4]==d[5]) break;
}
if(c[1]==d[1] && c[2]==d[2] && c[3]==d[3])
{
if(is)
{
c[1]=a1,c[2]=a2,c[3]=a3,c[4]=l1,c[5]=r1;
d[1]=b1,d[2]=b2,d[3]=b3,d[4]=l2;d[5]=r2;
}
else if(!is) ans+=2;
}
else ans+=(1<<x)*2;
}
int main()
{
fo(i,1,3) scanf("%d",&a[i]);
fo(i,1,3) scanf("%d",&b[i]);
sort(a+1,a+4);
sort(b+1,b+4);
pre();
int dep1=0,dep2=0;
while(c[4]!=c[5])
{
if(c[4]>c[5])
{
dep1+=turn(c[4],c[5]),c[4]=c[4]-turn(c[4],c[5])*c[5];
c[2]=c[1]+c[4];
c[3]=c[2]+c[5];
}
else if(c[5]>c[4])
{
dep1+=turn(c[5],c[4]),c[5]=c[5]-turn(c[5],c[4])*c[4];
c[2]=c[3]-c[5];
c[1]=c[2]-c[4];
}
}
while(d[4]!=d[5])
{
if(d[4]>d[5])
{
dep2+=turn(d[4],d[5]),d[4]=d[4]-turn(d[4],d[5])*d[5];
d[2]=d[1]+d[4];
d[3]=d[2]+d[5];
}
else if(d[5]>d[4])
{
dep2+=turn(d[5],d[4]),d[5]=d[5]-turn(d[5],d[4])*d[4];
d[2]=d[3]-d[5];
d[1]=d[2]-d[4];
}
}
if(c[1]!=d[1] || c[2]!=d[2] || c[3]!=d[3])
{
cout<<"NO";
return 0;
}
pre();
if(dep1>dep2)
{
int t=dep1-dep2;
ans+=t;
while(t)
{
if(c[4]>c[5])
{
if(t>=turn(c[4],c[5])) t-=turn(c[4],c[5]),c[4]=c[4]-turn(c[4],c[5])*c[5];
else c[4]=c[4]-t*c[5],t=0;
c[2]=c[1]+c[4];
c[3]=c[2]+c[5];
}
else if(c[5]>c[4])
{
if(t>=turn(c[5],c[4])) t-=turn(c[5],c[4]),c[5]=c[5]-turn(c[5],c[4])*c[4];
else c[5]=c[5]-t*c[4],t=0;
c[2]=c[3]-c[5];
c[1]=c[2]-c[4];
}
}
dep1=dep2;
}
else
{
int t=dep2-dep1;
ans+=t;
while(t)
{
if(d[4]>d[5])
{
if(t>=turn(d[4],d[5])) t-=turn(d[4],d[5]),d[4]=d[4]-turn(d[4],d[5])*d[5];
else d[4]=d[4]-t*d[5],t=0;
d[2]=d[1]+d[4];
d[3]=d[2]+d[5];
}
else if(d[5]>d[4])
{
if(t>=turn(d[5],d[4])) t-=turn(d[5],d[4]),d[5]=d[5]-turn(d[5],d[4])*d[4];
else d[5]=d[5]-t*d[4],t=0;
d[2]=d[3]-d[5];
d[1]=d[2]-d[4];
}
}
dep2=dep1;
}
if(c[1]==d[1] && c[2]==d[2] && c[3]==d[3])
{
cout<<"YES"<<endl;
cout<<ans;
return 0;
}
int p=log2(dep1);
fd(i,p,0)
jump(i,1);
jump(0,0);
if(c[1]==d[1] && c[2]==d[2] && c[3]==d[3])
{
cout<<"YES"<<endl;
cout<<ans;
}
else cout<<"NO"<<endl;
}