参考资料
题意简述
对非负整数三元组 定义一次操作:
给定 与 ,求把 变成 的最少操作次数,无法做到输出 。共 组,,。
解题思路
记 。注意 ,上式可改写为:
四种情形结果之和都是 ,故 是不变量; 之和不等即输出 。 时只有 ,答案为 。
在模 下观察:前三种情形恰是逐坐标乘 。以 为例,,且此时 , 已落在 内;故 。第四种情形是逐坐标乘 :。合起来,一次操作把三元组模 乘以 或 。
反过来,结果由「和恰为 」唯一确定。设三坐标模 的余数为 ,其和 ,即属于 :和为 时取 (对应乘 ),和为 时取 (对应乘 ),和为 时是某个顶点。于是施加 次操作的结果 ,就是与 或 同余、且和为 的唯一非负三元组——操作是确定的,从 出发只有一条轨迹。答案即最小的 使 ,否则 。
据此:
两式都对所有 成立,且组内三坐标取同一符号。
若 为偶:任意一次操作后三坐标全偶( 与 都是偶数)。又当三坐标全偶时「整组折半」与操作可交换,,于是 (在模 意义下)。所以走 步要求 全偶,且问题降为把 变到 。反复折半直到 为奇,并累加偏移量。若某步 仍偶而 不全偶,则无法再前进,就此停止。
为奇时 模 可逆。三条同余式中,第三条由前两条与「和 」自动成立(),只需:
用 大步小步算法(Baby-Step Giant-Step,BSGS)求最小 。设 ,把 写作 ():小步枚举 ,把 连同 存入哈希表;大步枚举 ,查 。两坐标各在 内,打包成单个键 即可(不超过 ,long long 可容)。 自小到大、相同键保留较大的 ,首个命中即最小 。
取 总是有效;取 仅当 三坐标全正——否则该符号对应的态和为 而非 ,并不等于 (含 坐标的 只能由乘 到达)。两者取较小。
顶点(两坐标为 、第三坐标为 )是不动点:非顶点到不了顶点,顶点也只停在自身,单独判掉。
每组数据折半 、BSGS 。时间复杂度为 。
参考代码
#include <bits/stdc++.h>
using namespace std;
using ll=long long;
const ll inf=0x3f3f3f3f3f3f3f3f;
tuple<ll,ll,ll> F(tuple<ll,ll,ll> a)
{
auto [x,y,z]=a;
ll s=x+y+z;
if(2*x>s)return {2*x-s,2*y,2*z};
if(2*y>s)return {2*x,2*y-s,2*z};
if(2*z>s)return {2*x,2*y,2*z-s};
return {s-2*x,s-2*y,s-2*z};
}
ll Pow(ll x,ll y,ll mod)
{
ll res=1;
x%=mod;
while(y)
{
if(y&1)res=(__int128)res*x%mod;
x=(__int128)x*x%mod;
y>>=1;
}
return res;
}
ll bsgs(ll a,ll b,ll c,ll d,ll s)
{
a%=s;b%=s;c%=s;d%=s;
if(a==c&&b==d)return 0;
ll m=sqrt(s)+1;
unordered_map<ll,ll> mp;
for(ll j=0;j<m;j++)
{
mp[c*s+d]=j;
c=c*2%s;d=d*2%s;
}
ll t=Pow(2,m,s);
for(ll i=1;i<=m;i++)
{
a=(__int128)a*t%s;b=(__int128)b*t%s;
if(mp.count(a*s+b))return i*m-mp[a*s+b];
}
return inf;
}
bool vtx(tuple<ll,ll,ll> a)
{
auto [x,y,z]=a;
return (x==0)+(y==0)+(z==0)>=2;
}
ll core(tuple<ll,ll,ll> a,tuple<ll,ll,ll> b,ll s)
{
if(a==b)return 0;
if(vtx(a)||vtx(b))return inf;
auto [x,y,z]=a;
auto [u,v,w]=b;
ll res=bsgs(x,y,u,v,s);
if(u&&v&&w)res=min(res,bsgs(x,y,(s-u)%s,(s-v)%s,s));
return res;
}
ll solve(tuple<ll,ll,ll> a,tuple<ll,ll,ll> b)
{
auto &[x,y,z]=a;
auto &[u,v,w]=b;
if(x+y+z!=u+v+w)return -1;
ll s=x+y+z;
if(!s)return 0;
ll ans=inf,cnt=0;
while(1)
{
if(a==b)ans=min(ans,cnt);
if(s&1)
{
ll c=core(a,b,s);
if(c<inf)ans=min(ans,cnt+c);
break;
}
if((u|v|w)&1)break;
a=F(a);
x/=2;y/=2;z/=2;
u/=2;v/=2;w/=2;
s/=2;cnt++;
}
return ans<inf?ans:-1;
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
int T;
cin>>T;
while(T--)
{
ll x,y,z,u,v,w;
cin>>x>>y>>z>>u>>v>>w;
cout<<solve({x,y,z},{u,v,w})<<'\n';
}
return 0;
}