题目描述
难度分:2100
输入T(≤104)表示T组数据。每组数据输入n、m(1≤m<n≤1018)。
你需要把n变成m。有如下操作,至多执行63次:
选择一个数x,满足0<x<n且0<n⊕x<n,然后把n变成x或者变成n⊕x。
如果无法把n变成m,输出−1。否则,设操作次数为k,首先输出k,然后输出 k+1个数,第一个数是初始n,其余k个数是每次操作后的n。
注意:你无需最小化操作次数。
输入样例
3
7 3
4 2
481885160128643072 45035996273704960
输出样例
1
7 3
-1
3
481885160128643072 337769972052787200 49539595901075456 45035996273704960
算法
构造
分以下情况讨论:
- 如果m<n且m⊕n<n,一次操作就可以完成,直接取x=m。
- 如果一次操作后能够得到k,那么m的二进制需要是k的子集,不然肯定无解。得到包含m的k之后,再操作一次把不一样的位消掉就可以得到m,因此在这种情况下两次操作就能达到目的。于是我们先构造k=m,然后考虑减小n⊕k。让k包含n最高位的1,这样一定可以满足n⊕k<n(消去n最高位的1能最大限度减小n),同时保证k也尽可能小,接下来判断k<n是否成立即可。
复杂度分析
时间复杂度
highbit操作获得n的最高位,时间复杂度为O(1),后面的构造+判断也都是O(1)的。因此,算法的时间复杂度为O(1)。
空间复杂度
仅使用了有限几个变量,额外空间复杂度为O(1)。
C++ 代码
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
int t;
LL n, m;
LL highbit(LL x) {
return x != (1ll<<62)? (1ull<<(63 - __builtin_clzll(x))): 1ll<<62;
}
void solve(LL n, LL m) {
if((n ^ m) < n) {
printf("1\n%lld %lld\n", n, m);
return;
}
LL k = m | highbit(n); // 让k有n最高位的1
if(k < n && (n^k) < n) {
if((m&k) != m) {
k ^= n;
}
printf("2\n%lld %lld %lld\n", n, k, m);
}else {
puts("-1");
}
}
int main() {
scanf("%d", &t);
for(int cases = 1; cases <= t; cases++) {
scanf("%lld%lld", &n, &m);
solve(n, m);
}
return 0;
}