[ARC222F] Triple Transformation
Genius_Star · · 题解
或许更好的阅读体验。
不是题。
思路:
因为你发现每次操作是唯一变换的,判定一个点能不能到另外一个点容易先想到能不能倍增第一个点找一下性质,即快速求出一个点变换
首先你发现操作特别抽象,考虑找一下性质;有一个比较显然的性质就是,每次操作的和是不变的,都是
更深层的,感觉每次变化的值没啥特别,具体的,例如第一个元素,只能变化成
于是考虑模
-
前面三种操作在模
S 意义下会变成(2x, 2y, 2z) 。 -
而最后一种操作会直接变成
(-2x, -2y, -2z) 。
于是
那么具体是哪个呢?(赛时做到这里可以直接猜最后只要是这两个之一就一定有解就可以迅速做完)
再分析下性质,对于上面的操作:
-
如果变成
(2x, 2y, 2z) ,显然三个数模S 后和还是S (因为只会将一个数减少另外两个数加的,而另外两个数加了后还是<S )。 -
而如果变成
(-2x, -2y, -2z) ,说明x, y, z < \frac{S}{2} ,于是误判到(2x, 2y, 2z) 后是不取模的,三个数的和是2S 。
所以在每一步
这样还是不好快速算走
-
具体的,设最后一次
-2 前是(x, y, z) ,后面是(-2^k x, -2^k y, -2^k z) 它们不会出现三个数都< \frac{S}{2} 的情况,所以误判后(2^k x, 2^k y, 2^k z) 不会出现三个数都> \frac{S}{2} 的情况。 -
而因为和是
2S ,所以至少要两个> \frac{S}{2} 的数,根据上面的限制;所以后面(2^k x, 2^k y, 2^k z) 是恰好有两个> \frac{S}{2} 的数,于是2S \cdot 2 - 2S = 2S ,和还是2S 不变,一直往后推可以归纳证明得到。
于是算
考虑尽量避免这种情况
-
如果
S 是偶数,那么经过一次操作后一定会全部变成偶数; -
而如果全部都是偶数,显然可以全部除以二处理是不影响的。
于是暴力做最多
所以只有初始是
-
如果只有一个
0 ,且S 是奇数,所以其它两个一定有一个大的,所以每次操作是全部+2 形的,前面的结论依旧适用。 -
如果有两个
0 ,显然操作后和本身相同,于是特判一下即可。 -
三个
0 ,一样特判。
特判后,只需要解决判定
这两个方程组,只要其中一个有解,那么就有解。
这不就是 BSGS 吗,先设定一个
于是预处理所有的
时间复杂度为
完整代码:
#include<bits/stdc++.h>
#define fi first
#define se second
#define lowbit(x) (x) & (-(x))
#define popcnt(x) __builtin_popcount(x)
using namespace std;
typedef unsigned long long ull;
typedef long long ll;
inline ll read(){
ll x = 0, f = 1;
char c = getchar();
while(c < '0' || c > '9'){
if(c == '-')
f = -1;
c = getchar();
}
while(c >= '0' && c <= '9'){
x = (x << 1) + (x << 3) + (c ^ 48);
c = getchar();
}
return x * f;
}
inline void write(ll x){
if(x < 0){
putchar('-');
x = -x;
}
if(x > 9)
write(x / 10);
putchar(x % 10 + '0');
}
struct Node{
int x, y, z;
inline bool operator<(const Node&rhs)const{
if(x ^ rhs.x)
return x < rhs.x;
if(y < rhs.y)
return y < rhs.y;
return z < rhs.z;
}
inline bool operator==(const Node&rhs)const{
return x == rhs.x && y == rhs.y && z == rhs.z;
}
};
map<array<int, 3>, int> mp;
inline int qpow(int a, int b, int mod){
int ans = 1;
while(b){
if(b & 1)
ans = 1ll * ans * a % mod;
a = 1ll * a * a % mod;
b >>= 1;
}
return ans;
}
int T, S, a1, a2, a3, b1, b2, b3, B;
inline void getnext(int &x, int &y, int &z){
int tx = 0, ty = 0, tz = 0;
if(y + z < x)
tx = x - y - z, ty = 2 * y, tz = 2 * z;
else if(x + z < y)
tx = 2 * x, ty = y - x - z, tz = 2 * z;
else if(x + y < z)
tx = 2 * x, ty = 2 * y, tz = z - x - y;
else
tx = y + z - x, ty = x + z - y, tz = x + y - z;
x = tx, y = ty, z = tz;
}
inline int solve(int X, int Y, int Z){
if(X == S || Y == S || Z == S)
return 2e9;
int inv2 = (S + 1) >> 1;
int mn = 2e9, id = -1;
for(int i = 0; i < B; ++i){
if(mp.count({X, Y, Z})){
int w = mp[{X, Y, Z}];
if(w < mn){
mn = w;
id = i;
}
}
// cerr << X << ' ' << Y << ' ' << Z << ' ' << mn << ' ' << id << '\n';
X = 1ll * X * inv2 % S, Y = 1ll * Y * inv2 % S, Z = 1ll * Z * inv2 % S;
}
if(id == -1)
return 2e9;
return mn * B + id;
}
inline void solve(){
a1 = read(), a2 = read(), a3 = read(), b1 = read(), b2 = read(), b3 = read();
S = (a1 + a2 + a3);
if(b1 + b2 + b3 != S){
puts("-1");
return ;
}
if(a1 == b1 && a2 == b2 && a3 == b3){
puts("0");
return ;
}
if((!a1) + (!a2) + (!a3) == 2){
puts("-1");
return ;
}
int ans = 0;
while((S & 1) ^ 1){
++ans;
getnext(a1, a2, a3);
// cerr << a1 << ' ' << a2 << ' ' << a3 << '\n';
if(a1 == b1 && a2 == b2 && a3 == b3){
write(ans);
putchar('\n');
return ;
}
if((b1 & 1) || (b2 & 1) || (b3 & 1)){
puts("-1");
return ;
}
a1 >>= 1, a2 >>= 1, a3 >>= 1, b1 >>= 1, b2 >>= 1, b3 >>= 1;
S >>= 1;
}
B = sqrt(S);
mp.clear();
int base = qpow(2, B, S);
for(int u = 0; u * B <= S; ++u){
if(!mp.count({a1, a2, a3})){
mp[{a1, a2, a3}] = u;
// cerr << "Yes\n";
}
// cerr << "ins: " << a1 << ' ' << a2 << ' ' << a3 << ' ' << mp[{a1, a2, a3}] << '\n';
a1 = 1ll * a1 * base % S, a2 = 1ll * a2 * base % S, a3 = 1ll * a3 * base % S;
}
// cerr << "B:" << b1 << ' ' << b2 << ' ' << b3 << '\n';
ans += min(solve(b1, b2, b3), solve(S - b1, S - b2, S - b3));
if(ans > 1e9){
puts("-1");
return ;
}
write(ans);
putchar('\n');
}
int main(){
T = read();
while(T--)
solve();
return 0;
}