[总结] 位运算

位运算中的优化

概念类

请保证您已经学会了位运算四大操作。

净运行时间约 30ms,比加法运算(约 40ms)快较多,是因为全是按二进制位计算。但加减与位运算关系并不大,所以利用位运算主要是利用左右移位的高速度。

位运算的操作基本上都能够在一个 \(clock type\) 中完成。(除法大概 20 个,加减法大概 2 个)

因此用位运算来卡常优化代码。

位压缩

比如状态压缩 DP 中的把状态压缩成二进制位的思想。

!(x&(x-1)) && x;//是否恰好只有一个true
x>>1&x;//是否有两个相邻的true
x>>2&x>>1&x;//是否有三个相邻的true

打包位统计

  • 对于 32 位的 \(unsigned\ int\)快速判断 true 的个数的奇偶性
x^=x>>1;x^=x>>2;
x^=x>>4;x^=x>>8;
x^=x>>16;

这样操作后,\(x\) 每一位的含义就变成了从第 \(i\) 位到第高位 true 数目的奇偶性。

假如要查询 \([l,r]\) 位这一段二进制数 true 的奇偶性,运用前缀和思想。

return (x>>l^x>>(r+1))&1;
  • 统计 true 的数目

提供平行算法。

基本思想就是邻位相加,二进制倍数分块处理。

inline int count(unsigned int x){
	int kase=0;
	x=(x&0x55555555)+(x>>1&0x55555555);
	x=(x&0x33333333)+(x>>2&0x33333333);
	x=(x&0x0f0f0f0f)+(x>>4&0x0f0f0f0f);
	x=(x&0x00ff00ff)+(x>>8&0x00ff00ff);
	x=(x&0x0000ffff)+(x>>16&0x0000ffff);
	return x;
}

具体来说可以看一个 \(217\) ,二进制下为 \(11011001\) 的计算过程。

begin : 11011001
Case 1: 10010101
Case 2: 00110010
Case 3: 00000101

因为一共有 \(8\) 位,按照 \(2,4,8\) 的单位块内运算,这也就对应了上面代码的前三个操作。

比如说 \(Case 2\)\(Case 3\) 的过程中,\(0011+0010=0101\),直接加到后面半个单位块,前面舍弃。

最后得到的数就是原数中 true 的个数。

  • 反转位的操作

\(\cdots\)

消除分支

  • 计算绝对值
int abs(int x){
    int y=x>>31;
    return (x+y)^y;
}
  • 求最大值
int max(int x,int y){
    int m=(x-y)>>31;
    return y&m|x&~m;
}
  • 交换两个变量
void swap(int &x,int &y){
    x^=y;y^=x;x^=y;
}
  • 计算两个整数平均数(不会溢出)
int ave(int x,int y){
    return (x&y)+((x^y)>>1);
}