位移运算

题目描述

给出两个数 a , b 。问 a 能否只通过位移运算( >>和 << 可以多次使用)变成 b 。如果可以输出 "Yes" ,否则输出 "No" 。

输入格式

第一行:一个数 t(1≤t≤100000) 。

第 2∼t+1 行:每行 2 个 a,b 中间用空格分隔( 0≤a,b≤10^9 )。

输出格式

输出共 t 行,对应答案的 "Yes" 和 "No"

样例

【样例输入】

复制代码
4
4 2
2 4
3 4
1 3

【样例输出】

复制代码
Yes
Yes
Yes
No
数据范围与提示

对于 20% 的数据, 1≤t≤50 , 0≤a,b≤20 ;

对于 40% 的数据, 1≤t≤2000 ;

对于 100% 的数据, 1≤t≤100000 , 0≤a,b≤10^9 ;

一些想法

先特别判断,如果 b 是 0,那么 a 一定可以位移为 0,所以直接输出 Yes,然后跳过剩余部分。

然后在常规代码中,可以先将 b 二进制后面的 0 都去掉,得到 b1,如果 b1 是 a 的前缀,说明 a 可以通过位移来变成 b。原因:因为,b 尾部的 0(如果有),是由尾部没有 0 的 b1 左移得来的。因为 b1 尾部没有 0 ,所以如果 a 可以通过右移让 b1 是他的前缀,那就一定可以继续右移变为 b1(将后面多余部分通过右移舍去,只剩下前缀部分)。如果 a 可以变为 b1,那么 a 就一定可以变为 b。(因为 b 是由 b1 左移的来的,那么 b1 就可以通过右移反过来得到 b)所以我们可以直接求 b 去掉尾部的 0 后是不是 a 的前缀子串。

具体实现:

1.去掉 b 尾部的 0,变成 b1。因为如果 b 的最后一位是 1(去掉了所有尾部的 0 后),那么 b1 一定是一个奇数(因为除了第一位 1 是奇数,其他位都是 2 的幂,只要最后一位是 1,就会是奇数),那用循环只要 b 还是偶数,就说明还没有去完,就继续右移(将右边也就是尾部的 0 移出去),直到得到尾部没有 0 的 b1。

2.然后判断 a 能不能通过右移得到 b1。如果 a 大于 b1,就将 a 右移 1。然后将 a 异或 b1 赋值给一个数(后面简洁一些)。当 b1 有 1 的地方 a 都有((a&b)==b,两边都有 1 为 1,如果两边都有 1 的地方等于 b1,因为无论怎么位移,1 的相对位置是不变的,只会改变位置或移出去,如果 b1 有 1 的地方 a 都有,那么 a 在一般情况下都能够通过位移得到 b1),因为可能会有特殊情况,虽然 (a&b)==b ,但是仍然不对,因为在这种情况:110011 & 10001=10001,但是这明显不对,后者不是前者的子串,所以我们还要多加一个判断条件,确保正确判断 b1 是否是 a 的子串。

用 a 异或 b1(就是前面得到的数) ,如果这个数最低位的 1 (-z&z,因为 -z 等于 z 的取反加 1,然后再按位和,这样会只剩下 z 最低位的 1)比 b1 最高位的 1 大,说明 a 和 b1 第一个不同的位置比 b1 大(最高位高),说明 b1 和 a 不同的第一位不在 b1 中,那 b1 就会是 a 的前缀子串,那就一定是对的。(或者 a 和 b1 一模一样,异或等于 0)。

大概就是这样了,然后看代码吧。

AC代码

cpp 复制代码
#include<bits/stdc++.h>
using namespace std;
int main(){
	int n;
	cin>>n;
	while(n--){
		int a,b;
		bool flag=false;
		cin>>a>>b;
		if(!b){
			cout<<"Yes"<<endl;
			continue;
		}
		while(!(b&1)) b>>=1;
		for(;a>=b;a>>=1){
			int z=a^b;
			if((a&b)==b&&(!z||(-z&z)>b)) flag=true;
		}
		if(flag==true) cout<<"Yes"<<endl;
		else cout<<"No"<<endl;
	}
	return 0;
}

易错点:

1.在特判 b 时忘记换行,导致错误......

2.特判 b 后为减少时间复杂度,用了 return 0......因为有多组数据,这组跳过了还有下一组,而 return 0 是直接结束整个程序,导致不会运行后面的组。所以这里我们要用 continue 跳过这一组数据的剩余部分,直接开始判断下一组数据。

还有注意大小写和换行就行了。

总结位运算技巧:

在本题出现了:

  1. ! (b&1), 当 b 还是偶数。用 b&1 来判断一个数的奇偶,等价于 b%2。

  2. b>>1 将 b 最低位去掉。

3.(-z&z),获得 z 的二进制最后一个 1。

相关推荐
风吹乱了我的头发~16 小时前
Day31:2026年2月21日打卡
开发语言·c++·算法
mjhcsp17 小时前
C++ 后缀平衡树解析
android·java·c++
D_evil__17 小时前
【Effective Modern C++】第六章 lambda表达式:33. 对于auto&&形参使用decltype以及forward它们
c++
-Rane18 小时前
【C++】vector
开发语言·c++·算法
希望之晨18 小时前
c++ 11 学习 override
开发语言·c++·学习
消失的旧时光-194319 小时前
智能指针(四):体系篇 —— 现代 C++ 内存管理全景图
开发语言·c++
仰泳的熊猫19 小时前
题目1531:蓝桥杯算法提高VIP-数的划分
数据结构·c++·算法·蓝桥杯
汉克老师19 小时前
GESP2023年12月认证C++二级( 第一部分选择题(1-8))
c++·循环结构·分支结构·gesp二级·gesp2级
刘琦沛在进步19 小时前
如何计算时间复杂度与空间复杂度
数据结构·c++·算法
消失的旧时光-194320 小时前
智能指针(三):实现篇 —— shared_ptr 的内部设计与引用计数机制
java·c++·c·shared_ptr