题目大意
存在无穷多个点,他们为1,2,3。。。假设一个点为a,另一个点为a+b,只有当a&b=b(二进制与运算)时,才存在一条路从a走到a+b,现在给任意两个数,判断是否可以从第一个数走到第二个数。
输入格式
第一行输入t(1<=t<=1e5),表示测试的个数,后面t行中,每一行输入两个数字u,v(1<=u,v<=2e30),表示起点和终点。
输出格式
如果可以达到则输出不区分大小写的"YES",如果不可以则输出"NO"。
样例
输入:
5
1 4
3 6
1 6
6 2
5 5
输出:
YES
YES
NO
NO
YES
题目的图片提示
思路
要做之前首先要先找到一个规律,如果a不变,要保证b满足a&b=b,那么b的二进制形式的每一位和a的每一位都有这样的对应关系:当a的某一位是1时,b对应的一位可以是1或者0,当a的某一位是0时,b对应的一位只能是0。此时,我们如果把a和b按位相加,会发现a原本是0的位依然是0,原本是1的位可能变成1或者0,抽象点来说,也就是满足a&b=b情况下a与b相加所得的数必定是a的原二进制基础下对数字是1的位进行左移(或者不移)所得到的。
理解这个规律后,题目就变成了:输入的u能否通过向左移动其中几个含1的位而得到v。举个例子:0110(十进制为6)把第二个1向左移动一位,就得1010(十进制为10),那么6和10之间就是连通的。
然后我们将输入的两个数转化为二进制后,从右向左判断是否v的每个1都存在u的1通过移位可以移动到对应的位置。
然后再简化一下思路,就变成了两个二进制数同时从最右位向最左位移动,同时记录两个二进制出现的1的个数u中出现的1我们存起来,当v中出现1时分给他,但如果出现u中存的1,不够分给v了,那就说明无法通过位移得到v。
然后贴一个cf上大佬的精简代码,我自己写的太拉了。
#include<iostream>
#include<cstdio>
using namespace std;
int main()
{
int t;
cin >> t;
while(t--)
{
int x,y;
cin >> x >> y;
int z = 0;
bool ok = (x <= y);
for(int i = 0;i < 30;i++)
{
if((x>>i)&1) z++;
if((y>>i)&1) z--;
if(z<0) ok=false;
}
if(ok) cout << "YES\n";
else cout <<"NO\n";
}
return 0;
}