C. Beautiful XOR
题目描述
给定两个整数 和 。你可以执行以下操作任意次数(包括零次):
- 选择一个整数 ,满足 ( 的当前值,而非初始值),
- 令 。这里的 代表位异或操作。
在执行一系列操作后,你希望将 的值恰好变为 。
请找出一个最多包含 100 个操作的序列(即每次操作中使用的 的值),将 转换为 ,或者报告无解。
注意,你不需要找出操作次数最少的序列,任何一个有效的、操作次数不超过 100 的序列即可。
解题思路
设初始值为 ,目标值为 。经过一系列选择整数 的操作后, 的最终值将变为 。令 。我们希望最终的值为 ,因此得到方程: 这意味着所有操作的异或总和必须为 。
我们的任务是找到一系列 的值,这些值是有效的(即在每一步都满足 ),并且它们的异或总和为 。
构造总异或和 的一个简单方法是将其分解为其二进制表示中的各个2的幂。任何整数都可以唯一地表示为不同的2的幂的和,这对应于其二进制表示中为1的位。如果我们对每一个这样的2的幂执行一次异或操作,它们的总异或和将等于 。
假设 ,其中每个 是一个不同的2的幂。我们可以用这个 序列作为我们的操作 。每次操作的条件是 。
我们来分析这个条件。设 是 的二进制表示中最大的2的幂。这对应于 和 不同的最高位。
情况一: (初始值) 如果转换所需的最大2的幂 小于或等于初始值 ,那么解是存在的。设所需的操作为 ,其中 。 在任何一步,当我们执行操作 时,我们需要确保 。 由于 是差异的最高位,所以 在高于 的所有位都与 相同。条件 意味着 的最高位至少与 在同一位置。 当我们应用 的操作时,这些操作只影响比 的最高位低的位。因此, 的值仍然大于或等于 。这确保了对于任何后续操作 ,。因此,序列中的所有操作都是有效的。
情况二: (初始值) 如果转换所需的最大2的幂 大于初始的 ,则不可能达到 。 设 。条件 意味着 。这说明 在位置 及以上的所有位都是0。 对于任何操作,我们都必须选择一个 。如果 ,那么也必须有 。异或操作的结果 也将小于 。这是因为两个数的异或结果的最高位不会超过这两个数中较大者的最高位。 由于我们从 开始,我们永远无法达到一个大于或等于 的值。 然而,由于 是 的一部分,所以 和 的第 位必须不同。由于 的第 位是0,所以 的第 位必须是1。这意味着 。 由于我们永远无法达到 的值,因此不可能将 转换为 。
所以,策略是:
- 计算 。
- 如果 ,则不需要任何操作。
- 找到 中最大的2的幂,设为 。
- 如果 ,解就是构成 的所有2的幂的序列。
- 如果 ,则无解。
查看代码
#include <algorithm>
#include <cmath>
#include <cstdint>
#include <iostream>
#include <map>
#include <numeric>
#include <set>
#include <tuple>
#include <vector>
using namespace std;
#define int long long
// #define mod 1000000007
#define N 500005
void solve() {
int a, b;
cin >> a >> b;
int x = a ^ b;
vector<int> ans;
int pow = 1;
if (x == 0){
cout << 0 << endl;
return;
}
while(x > 0) {
if(x & 1) {
ans.push_back(pow);
}
pow *= 2;
x >>= 1;
}
if (ans.back() <= a ) {
cout << ans.size() <<endl;
for (int i = 0; i < ans.size(); i++) {
cout << ans[i] << " ";
}
cout << endl;
} else {
cout << -1 << endl;
}
}
int32_t main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
cout.tie(nullptr);
#ifdef DEBUG
freopen("./input.txt", "r", stdin);
#endif
int t;
cin >> t;
while (t--) {
#ifdef DEBUG
static int test_num = 1;
cout << "test case: " << test_num++ << endl;
#endif
solve();
}
}