异或题
目录
题意:
给你 n , m,让你对 n 做异或(^)操作,使其变为 m 。
但是你对 n 异或的数 y 得满足两个条件:
-
0 < y < n
-
0 < y^n < n
思路:
异或很烦,两个条件更烦。
对条件进行探究得出一些规律后,分类讨论。
性质1:
比如
n = 1101
m = 0001
我们可以把n的1变成0,
因为n的任意一个二进制位都是小于n的,而对1异或会使这一位变成0,即异或的结果是变小的
满足条件1,2。
性质2:本题核心
(这个我当时想到了,但是没有接着向后系统地分析)
n = 11000
m = 00010
我们可以让n异或01111,符合条件1、2,
所以n = 10111,接着用性质1即可得到m。
这种操作牺牲一个前位1使其后面全为1
(前位就是前面位置的意思,为了方便略写)
性质3:关于n的首位被异或
- 结果为0
结果肯定小于n,但这个数得小于n哦。
- 结果为1
这个数肯定小于n,但是结果可能大
于是分类讨论:
(n > m 这种条件就不多说了吧)
1.首位相同
n = 1010
m = 1001
可以直接得到,n 异或上 0011
(性质3,0始的异或值肯定小,而异或结果m本来就小于n,满足条件1、2)
2.首位不同且n只多一个前位1
n = 10
m = 01
n = 1001
m = 0010
这种的就没有结果,不满足条件1、2。
3.首位不同且多前位1
n = 1100
m = 0010
用性质2随便转了,先转成牺牲一个前位1使其后面全为1的,然后就能用性质1直接转为m
当n的第二个1和m的第一个1同一位时:
n = 1010101
m = 0011001
这个没法性质2,但对n异或1001101可以直接得到m,类似情况1了。
参考代码:
#define ll long long
#define endl "\n"
#define int long long
const ll inf = 1e9;
const ll MOD = 998244353;
void solve()
{
int n, m;
cin >> n >> m;
//2的10次方是3颗0
// 60 18
int cnt1 = 0, cnt2 = 0;
int i;
for (i = 62; i >= 0; i--)
{
if (n & (1ll << i))cnt1++;
if (m & (1ll << i))
{
cnt2++; break;
}
}
vector<int>ret;
if (cnt1 == cnt2)
{
if (n & (1ll << i))//首位相同
{
ret.push_back(n);
ret.push_back(m);
}
else
{
cout << -1 << endl;
return;
}
}
else
{
if (cnt1 > cnt2 + 1||!(n & (1ll << i)))
{
ret.push_back(n);
int k = -1;
for (int j = 62; j >= 0; j--)
{
if ((n & (1ll << j)))
{
if (k == -1)
k = 0;
else
{
k = j; break;
}
}
}
int tmp = 0;
for (int j = 0; j <= k; j++)
{
tmp |= (1ll << j);
}
ret.push_back(tmp);
if(tmp!=m)///
ret.push_back(m);
}
else
{
ret.push_back(n);
ret.push_back(m);
}
}
//
cout << ret.size()-1 << endl;
for (int x : ret)
{
cout << x << " ";
}
cout << endl;
}
signed main()
{
ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
int t = 1;
cin >> t;
while (t--)
{
solve();
}
return 0;
}