位运算是状态压缩DP的基础。
算符
C/C++中的位运算算符(按优先级排序):
红色算符与位运算算符的优先级高低顺序容易弄混,这里强调一下。
! ~ (bitwise NOT) 按位非 ~x
* / %
+ -
> >= < <=
<< >> (bitwise left shift and right shift) 按位左移、右移 x>>y x>>=y
== !=
& (bitwise AND) 按位与 x&y x&=y
^ (bitwise XOR) 按位异或 x^y x^=y
| (bitwise OR) 按位或 x|y x|=y
基本操作
1、判断x的第i位 x & 1<<i
2、将x的第i位置0 x & ~(1<<i)
3、将x的第i位置1 x | 1<<i
4、求x的最低位的1 lowbit(x) x & -x
枚举子集
我们知道,可以用一个整型(例如 int、long long)变量 $s$ 表示一个大小不超过该整型位数的全集 $U$ 的某个子集 $S$。现在考虑如何枚举一个用整数 $s$ 表示的集合的所有子集。首先 $s$ 的子集的值必不大于 $s$,所以可以举 $[0,s]$ 的整数,判断其是否为 $s$的子集,复杂度为 $O(s)$。还有一种更好的做法,复杂度为$s$ 的子集个数。
for(int s=u; ; s=(s-1)&u){ // do something with s if(s == 0) break; }
枚举大小为 $k$ 的子集
注意:要求 $k > 0$。
int comb = (1 << k) - 1; while (comb < 1 << n) { // do sth with comb int x = comb & -comb, y = comb + x; comb = ((comb & ~y) / x >> 1) | y; }
枚举超集
for (int s = u; s <= tot; s = (s + 1) | u) { // do something with s }