题意:给你一个n和x,问你是否存在一个m,使
n&(n+1)&(n+2)&...&m=x如果存在输出最小的m,否则输出-1。
思路:因为是与运算,那么与之后的结果一定不会大于n,所以如果m>n就可以直接输出-1.
而且数据范围比较大,那么我们可以把n转化为二进制数。
假如是90转换为二进制数就是1011010,
&n+1即1011010&1011011=1011010,
&n+2即1011010&1011100=1011000,
&n+3即1011000&1011101=1011000,
&n+4即1011000&1011110=1011000,
&n+5即1011000&1011111=1011000,
&n+6即1011000&1100000=1000000,
好那么到这里我们可以发现,每次与的结果只能为从高位开始的1的连续子区间的前缀
(比如1011010可以得到1011000但是不能得到1000010(如果要前面的1为0那么后面的1一定会提前为0)
也不能得到1010000(如果要第三个位置为1第四个为0那么与的数第四个位置也要为0,但是如果为0那么就会进位导致第三位置的1也会0))
并且,而且二进制数如果x中为1但是n不为1那么无论怎么加也不能使当前位为1.
于是不能得到的情况就处理完了,现在就看如何得到最小的m。
那么我们也可以从上图发现,最小的m就是从高到低起,x的二进制位和n的二进制不同的i前面一位(比如要得到1000000,那么与1011000不同位为第3位,那么就要往前挪一位&1100000就可以得到)。
代码:
#include<cstdio>
#include <iostream>
#include <algorithm>
#include <string.h>
#include <string>
#include <math.h>
#include<vector>
#include<queue>
#include<map>
#define sc_int(x) scanf("%d", &x)
#define sc_ll(x) scanf("%lld", &x)
#define pr_ll(x) printf("%lld", x)
#define pr_ll_n(x) printf("%lld\n", x)
#define pr_int_n(x) printf("%d\n", x)
#define ll long long
using namespace std;
const int N=1000000+100;
ll n ,m,h;
int a[N],b[N];
void solve()
{
for(int i =1;i<=70;i++)//初始化
a[i]=b[i]=0;
ll x,y;
cin>>x>>y;
n=x,m=y;
if(m>n)
{
cout<<"-1\n";
return ;
}
if(m==n){
cout<<m<<endl;
return ;
}
int sizea=1,sizeb=1;
for(int i =1;i<=70;i++)//从小到大转换为二进制到数组里面
{
if(n%2){
a[i]=1;
sizea=i;
}
if(m%2){
b[i]=1;
sizeb=i;
}
n/=2,m/=2;
}
bool ii=0;
for(int i =max(sizea,sizeb);i>=1;i--)
{
if(a[i]!=b[i])
{
if(b[i]){
cout<<"-1\n";
return ;
}
else ii=1;//前缀只能到这个位置
}
else{
if(a[i]==1&&ii) //后面有1并且前面有0了
{
cout<<"-1\n";
return ;
}
}
}
for(int i =max(sizea,sizeb);i>=1;i--)//找连续的1的区间
{
int j=i;
if(a[j]==1){
int sum=0;
while(a[j]==1)
{
if(b[j]==0&&j>=1) sum++;
j--;
}
if(sum!=i-j&&sum!=0){
cout<<"-1\n";
return ;
}
}
i=j;
}
for(int i =max(sizea,sizeb);i>=1;i--)//从最高位找不同的
{
if(a[i]!=b[i]&&a[i]==1)
{
b[i+1]=1;
break;
}
}
ll res=0,t=1;
for(int i =1;i<=70;i++)
{
if(b[i]==1)
res+=t;
t*=2;
}
cout<<res<<endl;
return ;
}
int main()
{
int t;
sc_int(t);
while(t--)solve();
return 0;
}
/*
101010
011111
1010 1011 1100 1101 1110 1111
1010
1000
*/