位运算
运算符
| 符号 | 语法 | 名称 | 含义 |
|---|---|---|---|
| & | \(a\) & \(b\) | 按位与 | 均为 \(1\) 时才是 \(1\),否则是 \(0\);只要有 \(0\),就是 \(0\) |
| \(\mid\) | \(a\mid b\) | 按位或 | 均为 \(0\) 时才是 \(0\),否则是 \(1\);只要有 \(1\),就是 \(1\) |
| ^ | \(a\) ^ \(b\) | 按位异或 | 相同是 \(0\) ,不同是 \(1\);\(1\) ^ \(1=0\) ^ \(0=0\),\(1\) ^ \(0=0\) ^ \(1=1\) |
| ~ | ~ \(x\) | 取反 | 每一位都取相反数 |
| << | \(a\) << \(val\) | 左移 | 向左移,不足补 \(0\),多余去除 |
| >> | \(a\) >> \(val\) | 右移 | 向左移,不足补 \(0\),多余去除 |

特别地,对于负数的右移运算:
1 2 3 4 5 6 7 8 9 | |
常用技巧
乘/除以 2 的幂次
- 除:
257>>4等同于257/pow(2,4); - 乘:
3<<4等同于3*pow(2,4); - 常用
a<<=1代表a*=2;常用a>>=1代表a/=2。
1 2 3 4 5 6 7 8 | |
同样地,特别注意右移对于负数的取整方式,负数的左移:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | |
交换两数
交换两数建议使用 swap() 函数(非常强大),或者定义第三个变量临时存储,较为常用。
使用和差交换或者位运算交换可能会导致溢出,不建议使用。
1 2 3 | |
1 2 3 | |
这里用到的按位异或性质:
- 相同的数按位异或为 \(0\)
- 任何数按位异或 \(0\) 都是它本身
- 按位异或符合交换律、结合律
第7行展开写是 b' = a' ^ b' = (a ^ b) ^ b = a ^ (b ^ b) = a ^ 0 = a;
第8行展开写是 a' = a' ^ b' = (a ^ b) ^ a = b ^ (a ^ a) = b ^ 0 = b。
(没带 ' 的是原始变量)
常见应用
- 二分查找中,对于左边界 \(l\) 和右边界 \(r\),使用
(l + r) >> 1或l + (r - l) >> 1(后者减小溢出情况)来计算中间点 \(m = \frac{l + r}{2}\); - 树状数组中,使用
x&(-x)代表lowbit(x); - 线段树中,对于存储在 \(d_p\) 中的节点,它的两个子节点的下标分别为
p >> 1和p >> 1 | 1。
基本例题
判断是否是 2 的幂
力扣链接:231. Power of Two
一个数是 \(2\) 的幂,其二进制必须是一个 1 加上若干个 0 ,如 1000 。满足这个数减去 \(1\) 后每一位都与原数不同。如对于 \(2\) 的 \(4\) 次方 \(16\) :
1 2 3 4 | |
原理可参考 #法1 - 按位与运算。
1 2 3 4 5 6 | |
二进制中 1 的个数
法1 - 按位与运算
与 #判断是否是 \(2\) 的幂 类似,通过语句 a&(a-1) 可以消去这个数二进制最低位的 1 ,以 \(14\) 举例:
1 2 3 4 | |
可以看到,减去 \(1\) 后与前面的部分(011)无关(上下每位都相同),后面的部分每位都上下不同。按位与这两个数会保留前面的部分,后面的部分全部置 0,因此涉及到的一个 1 被消为 0。
因此,可以得出:
- 消去一个数二进制最右侧的
1:a&(a-1); - 获取一个数二进制最右侧的
1:a&(~a+1)或a&(-a)(即lowbit(x))。
-a 表示 a 的补码。
1 2 3 4 5 6 7 8 9 10 11 | |
法2 - 每一位判断
每次判断最低位是否为 1,并右移移掉最低位,直到移为 \(0\)。
1 2 3 4 5 6 7 8 9 10 11 | |
转化成不用位运算的方式:
1 2 3 4 5 6 7 8 9 10 11 | |
二进制是否 0 1 交替
力扣链接:693. Binary Number with Alternating Bits
每次判断相邻的两位是否是 01 或者 10。
通过对该数按位与运算 \(3\) 即可( \(3\) 对应的二进制为 11)。如果该数最后两位为 10,则按位与结果为 10;最后两位为 01,则按位与结果为 01。否则都不满足。
然后把该数右移 \(1\) 位,继续检查最后两个二进制位。
1 2 3 4 5 6 7 8 9 10 11 | |
练习题
LCsingle-number-iii
P1226
洛谷链接:P1226 【模板】快速幂
AC code
| 快速幂模板 | |
|---|---|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | |
P10118
洛谷链接:P10118 『STA - R4』And
STD 题解
from: 『STA - R4』 T1 题解
可以发现,\(x \operatorname{AND} y\) 对应了在二进制加法中进位的位置集合,\(x \operatorname{XOR} y\) 对应了结果中为 \(1\) 但是没有进位的位置集合。因此,通过用位运算模拟加法的过程,我们可以得出
因为已知 \(x + y\) 和 \(x \operatorname{AND} y\),因此可以得到 \(x \operatorname{XOR} y\),设为 \(C\)。
那么所有可能的合法数对可以通过将 \(C\) 二进制下的 \(1\) 分配给 \(x\) 或 \(y\) 得到。注意到若 \(C \operatorname{AND} B\) 不为 \(0\) 那么不存在合法的分配方案,此时应按无解处理。
通过一些观察可以发现 \(C\) 二进制下最高位的 \(1\) 一定分配给 \(y\),否则无法保证 \(x \le y\)。在这之后的所有情况均合法,所以可以发现对于一种方案,将 \(C\) 二进制下除最高位的其他位分配方案取反,得到的方案也是合法的,且与原方案互补。
所以只会有 \(C\) 二进制下最高位的 \(1\) 产生贡献,贡献系数为剩余位数的方案数,即 \(2 ^{\operatorname{popcount}\left(C\right) - 1}\)。
因此我们可以在 \(\mathcal{O}\left(1\right)\) 的时间内回答每组询问。