常用技巧:位运算的奇技淫巧

整理一些位运算的常用操作。基础知识点提醒:

1.~与-的不同含义。~是逐位取反,而-是算术运算取负。

2.>>右移运算,对于无符号数填充0;有符号数则填充当前符号

重要!运算符优先级的整理!涉及到位运算一定要考虑好优先级的问题,要不然会出现各种奇怪的问题功亏一篑。为保证正确性,建议严格使用括号

  1. ()、[]、.、->
  2. ~(逐位取反)、-(取负)、!、*(取值)、&(取地址)、++、--、sizeof
  3. /、*、%
  4. +、-
  5. <<、>>
  6. >、<、>=、<=
  7. ==、!=
  8. &
  9. ^
  10. |
  11. &&
  12. ||
  13. ?:
  14. =、+=、-=、*=、/=、%=、<<=、>>=、&=、|=、^=

接下来是位运算常见的应用。不过个人感觉除过零散的数学题,位运算很多是被应用于状压DP,所以状压DP相关的位运算会在状压DP部分也整理一份

1.乘除法:左移右移实现2^k的乘除操作

2.通过位操作交换两数

void swap(int &a,int &b){
    a=a^b;
    b=a^b;
    a=a^b;
}

3.位操作判断奇偶数

x&1==0

4.求绝对值

int abs(int x){
    int i=x>>31;    //i取值为0或-1
    return i==0?a:~a+1;
    //或者return ((a^i)-i),对于i=0,a^i=a^0=a;对于i=-1,a^i=a^-1=a^0xffff=~a
}

5.高低位交换

比如说,给定一个 16 位的无符号整数,将其高 8 位与低 8 位进行交换,求出交换后的值。

unsigned shoet x=3450;
x=(x<<8)|(x>>8)

6.找到二进制中1出现的最低位

a&-a   //也就是lowbit函数,树状数组要用

7.统计二进制中1的个数

int count=0;
while(x){
    x=x&(x-1);   //每计算一次,x的二进制中的低位就少一个1
    count++;
}

同样的思路,该技巧还可以用于处理:

1)O(1)判断一个数是不是2的幂次:判断数中是不是只有一个“1”

2)计算要将A转化为B需要改变几位:C=AxorB,再看C中有几个“1”

8.二进制进行子集枚举

使用一个正整数二进制,第i位是1还是0代表集合的第i个数取或者不取。所以从0到2^n-1总共2^n个整数,正好对应集合的2^n个子集。对此进行拓展有下列操作:

1)要求集合中不能有两个相邻的元素

if ((mask >> 1) & mask) continue;

2)在限定必须不取某些元素的前提下枚举子集

// mask的第x位为0表示x必须不在子集中(原集合中不含这个元素)
for (int mask1 = mask; mask1 >= 0; mask1 = (mask1 - 1) & mask) 

3)在限定必须取某些元素的前提下枚举子集

// mask的第x位为1表示x必须在子集中
for (int mask1 = mask; mask1 < (1 << m); mask1 = (mask1 + 1) | mask)

9.利用a^b^b=a

1)数组中,只有一个数出现一次,剩下都出现两次,找出出现一次的数

数组中所有数依次异或,最后结果即为答案

2)数组中,只有两个数出现一次,剩下都出现两次,找出出现一次的

假设这两个数分别是x和y.将数组全部异或,最终结果res=x^y。因为x!=y,res!=0。查看res,找出其二进制某一位为1,该位即为a。根据数组中每个数的a位是否为1可分为两组,采用1)中的方法分别求出两组的结果即可。

参考链接:https://www.zhihu.com/question/38206659

posted @ 2020-10-09 15:13  太山多桢  阅读(350)  评论(0编辑  收藏  举报