C++位运算及其应用

sdxjzsq

2018-08-12 21:05:13

Personal

## 为什么会写这篇blog? 位运算是用来进行玄学优化的一种非常好的途径,特别是在wys大佬的“浅谈计算机系统结构与OI种的使用编程技巧”中体现得淋漓尽致!我就是在看这个pdf的过程中突然发现自己已经不太能记得位运算的一些规则,故搜索之,得到一些便于理解的例子,记下来。 ## 位运算! 参考资料:[来谈谈C++ 位运算 & | << >> ^ ~ %](https://blog.csdn.net/fox64194167/article/details/20692645) ### 0.位运算一定记得加括号!!!他的优先级最小!!! ### 1. & 按位与 只有对应的二进制位都为1结果为1,否则为0. ### 2. | 按位或 对应的二进制位中只要一个为1,则结果为1,否则为0. ### 3. << 左移 x<<n表示x向左移动n位,即: $x<<n==x\times 2^n$ ### 4. >> 右移 x<<n表示x向右移动n位,即: $x>>n==x\div 2^n$ (除法结果保留整数) ### 5. ^ 按位异或 对应的二进制位相同,则结果为0,否则为1. 为了便于理解异或,一定要用这个题目: 一个数组中,所有数字都出现了两次,只有一个没有,请找出这个数字。 统计每个数字次数?NO!只要异或就够了! 只需要想下面这样写: ```cpp for(int i=1;i<=n;i++) a[0]^=a[i]; ``` 最后a[0]便是题目的答案。 为什么呢?因为两个相同的数异或的结果为0,而异或具有交换律,因此最后剩下的就是只出现一次的数了。 借用上面参考资料中的话来说: **异或是嫉妒成双成对的。** ### 6. ~ 取反 他的结果与当前二进制位上的数相反(0变成1,1变成0) 他满足:$x-y==x+\sim y+1$ 因此$\sim y==-y-1$ 比如$\sim 11==-11-1==-12$ ## 单个位运算符号的妙用! ### 1. & 我们经常要用的是否被2整除,一般都写成 `if(n % 2 == 0)` 可以换成 `if((n&1) == 0)` ### 2. | (复制自参考资料) 可以用一个unsigned int 来存储多个布尔值。 比如一个文件有读权限,写权限,执行权限。看起来要记录3个布尔值。我们可以用一个unsigned int也可以完成任务。 一个数r来表示读权限,它只更改个位来记录读权限的布尔值 0000000**1** (表示有读权限) 0000000**0** (表示没有读权限) 一个数w表示写权限,它只用二进制的倒数第二位来记录布尔值 000000**1**0 (表示有写权限) 000000**0**0 (表示没有写权限) 一个数x表示执行权限,它只用倒数第三位来记录布尔值 00000**1**00 (表示有执行权限) 00000**0**00 (表示没有执行权限) 那么一个文件同时没有3种权限就是 ~r | ~ w | ~ x 即为 00000000,就是0 只有读的权限就是 r | ~w | ~x 即为 00000001,就是1 只有写的权限就是 ~r | w | ~x 即为 00000010,就是2 ... 一个文件同时有3种权限就是 r | w | x 即为 00000111,就是7 ### 3. << 和 >> 用来代替$\times 2$和$\div 2$的运算 ### 4. 使用<<和>>代替% 比如求x%8. 按照%的定义, $a\%b==a-\lfloor {a\over b}\rfloor\times b$ 我们还知道,$\lfloor{x\over 8}\rfloor ==\lfloor{x\over 2^3}\rfloor==x>>3$ 因此, $x\%8==((x>>3)<<3)$ ### 5. ^用于交换两个数的值 ```cpp void swap(int *a,int *b) { *a^=*b; *b^=*a; *a^=*b; } ``` ## 综合运用位运算进行玄学加速! (摘自wys大佬的“浅谈计算机系统结构与OI种的使用编程技巧”) ### 1. 读某个二进制位 ``` cpp inline int read_bit(int x,int pos) { return (x>>pos)&1; } ``` ### 2. 将某个二进制位置为1 ``` cpp inline int set_bit(int x,int pos) { return x|(1<<pos) } ``` ### 3. 将某个二进制为置为0 ``` cpp inline int clear_bit(int x,int pos) { return x&~(1<<pos); } ``` ### 4. lowbit ``` cpp inline int lowbit(int x) { return x&-x; } ``` ### 5. 判断是否是二的幂次 ``` cpp inline bool is2(int x) { return !(x&(x-1)); } ``` ### 6. 状压DP - 判断一个数字x二进制下第i位是不是1 ``` cpp if((x>>i)&1>0) ``` 这实际上就是我们第1条 - 将一个数字x二进制下第i位更改为1 这实际上就是第2条 - 把一个数字二进制下最靠右的第一个1去掉。 ```cpp x=x&(x-1) ``` 这个也可以用来判断x是否是2的次幂: ```cpp if((x&(x-1))==0) ``` - 枚举状态S的子集 ``` cpp for(int i=S;i;i=(i-1)&S) work(i); ``` 大概就是这些了... (其实还有bitset,但是因为用不到所以不写了) 位运算万岁!!!