Codeforces Round 628 (Div. 2) D. Ehab the Xorcist
题目大意
给出两个数 \(u\) 和 \(v\),构造一个最短的数组满足数组的异或和为 \(u\),和为 \(v\),多解输出任意解,不存在解输出 -1。
\((0\leq u,v\leq 1e18)\)
题目分析
先考虑一些特殊情况:
1、不存在解
2、\(u=v~\and~ u\neq0\)
3、\(u=v~\and~u=0\)
情况 2、3 很好处理,答案显然,特判即可,但如何判断不存在解的情况呢?
我们知道异或是不进位的二进制加法,所以显然有 \(u\leq v\),因此当 \(u>v\) 时一定无解。除此之外就一定有解了吗?并不是。
我们知道位运算有个经典套路:\(a\oplus b=a|b-a\&b,~a+b=a|b+a\&b\),所以 \(a+b-(a\oplus b)=2(a\&b)\),证明留待读者自己完成。这个我真的会证,不过博客是写给自己看的所以就不多逼逼了
易知一定不存在两个数满足它们的异或和与和之差为奇数,又因为异或和加法运算都满足结合律和交换律,所以 \(x_1,x_2,x_3,\dots,x_n\) 的异或和一定可以转化为两个数 \(a,b\) 的异或和,它们的总和也一定能转化为 \(a,b\) 之和,对于两个数的情况不存在解,对于多个数的情况也不可能存在解,从而当 \((v-u)\%2\neq0\) 时,无解。
当 \((v-u)\%2=0\) 时,令 \(p=\dfrac {v-u}2=a\&b,~q=a|b\),显然存在一组长度为 3 的解 \(\{u,p,p\}\) 满足 \(u\oplus p\oplus p=u,~u+p+p=v\), 但我们要求的是最短的数组,长度为 3 一定是最短吗?有没有长度为 2 的呢?显然是有的。看样例就知道肯定有
假设存在两个数 \(a,b\) 满足 \(a\oplus b=u,~a+b=v\),根据上面提到的套路,我们有 \(p=a\&b=\dfrac {v-u}2,~q=a|b=v-p\)。
\(p,q\) 已知,则我们可以通过枚举 \(p\) 和 \(q\) 在二进制下的每一位从而确定 \(a\) 和 \(b\)。
假设这一位为 \(i\),则有四种情况:
1、\(p_i=q_i=0\),则 \(a_i=b_i=0\);
2、\(p_i=0,~q_i=1\),则 \(a_i=1,~b_i=0\)(反过来也行,一样滴);
3、\(p_i=1,~q_i=0\),显然不存在两个数 \(a,b\) 满足 \(a\&b=p,~a|b=q\);
4、\(p_i=q_i=1\),则\(a_i=b_i=1\)。
判到 \(q\) 的最后一位停止,如果没出现过情况 3,则存在更短的解 \(\{a,b\}\);否则最短解长度为 3,直接输出 \(\{u,p,p\}\)。时间复杂度 \(O(\log p)\)。
至此题目就做完了,具体实现细节可参考下方代码。
代码实现
#include<bits/stdc++.h>
using namespace std;
int main(){
long long u,v;
cin >> u >> v;
if(u==v){
if(u==0){
cout << 0;
}
else {
cout << 1 << endl << u;
}
}
else if(u>v){
cout << -1;
}
else {
long long p=v-u;
if(p&1){
cout << -1;
}
else {
p/=2;
long long q=v-p;
int flag=1;
long long a=0;
for(unsigned long long i=1;i<=q;i<<=1){
if(p&i){
if(q&i){
a+=i;
}
else {
flag=0;
break;
}
}
else if(q&i) a+=i;
}
if(flag){
printf("2\n%lld %lld",a,v-a);
}
else {
printf("3\n%lld %lld %lld",u,p,p);
}
}
}
}
