位移运算

题目描述

给出两个数 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。

相关推荐
hurrycry_小亦3 小时前
洛谷题目:P1365 WJMZBMR打osu! / Easy 题解(本题较简)
c++
m0_748708053 小时前
C++代码移植性设计
开发语言·c++·算法
郝学胜-神的一滴3 小时前
Linux Socket模型创建流程详解
linux·服务器·开发语言·网络·c++·程序人生
王老师青少年编程3 小时前
2024信奥赛C++提高组csp-s复赛真题及题解:决斗
c++·真题·csp·信奥赛·csp-s·提高组·决斗
「QT(C++)开发工程师」3 小时前
C++ 观察者模式
java·c++·观察者模式
m0_706653233 小时前
高性能网络协议栈
开发语言·c++·算法
永远睡不够的入3 小时前
类和对象(上)
开发语言·c++·算法
橘颂TA3 小时前
【剑斩OFFER】算法的暴力美学——力扣 207 题:课程表
数据结构·c++·算法·leetcode·职场和发展
孞㐑¥3 小时前
算法—链表
开发语言·c++·经验分享·笔记·算法