ACM入门之【位运算】

162 阅读3分钟

介绍

位运算是对整数在二进制层面进行操作的运算方式。计算机内部所有数据都是以二进制形式存储的,所以位运算是计算机处理数据最底层、最快速的一种方式。 在竞赛中,位运算可以大幅提高效率,常用于:

  • 状态压缩(DP)
  • 异或技巧
  • 快速判断特定位

位运算符对照表

运算符名称示例(对二进制)说明
&与(AND)0101 & 0011 = 0001两个都是1,结果才是1
|或(OR)0101 | 0011 = 0111有一个是1就为1
^异或(XOR)0101 ^ 0011 = 0110不同为1,相同为0
逻辑非(Logical NOT)!123 = 0 , !0=1如果值为0,结果为 1.如果值为非0,结果为 0
~非(NOT)~0101 = 1010(取反)所有位取反
<<左移0101 << 1 = 1010向左移一位,相当于乘2
>>右移0101 >> 1 = 0010向右移一位,相当于除2


功能示例式子
去掉最后一位10110 → 1011x >>> 1
在最后添加 01011 → 10110x << 1
在最后添加 11011 → 10111(x << 1) | 1
右数第 k 位变成 1100011 → 101011,k = 4x | (1 << (k - 1))
右数第 k 位变成 0101011 → 100011,k = 4x & ~(1 << (k - 1))
获取右数第 k 位101011 → 1,k = 4(x >> (k - 1)) & 1
截取最后 k 位101011 → 1011,k = 4x & ((1 << k) - 1)
把右边连续的 1 变成 0101011 → 101000x & (x + 1)
把右边第一个 0 变成 1101011 → 101111x | (x + 1)
把右边连续的 0 变成 1101000 → 101111x | (x - 1)
把右起第一个 1 变成 0101000 → 100000x & (x - 1)
取右边连续的 1101111 → 1111(x ^ (x + 1)) >> 1

常用板子

判断一个数是否是2的幂次。
bool f(int x)
{
    if( x & (x-1) == 0 ) return true;
    return false;
}
计算一个数2进制有多少个1int f(int x)
{
    int cnt=0;
    while(x)
    {
        x-=x&(-x);
        cnt++;
    }
    return cnt;
}
得到二进制最右边的1和后边0所构成的数。
int lowbit(int x)
{
    return x&(-x);
}
遍历所有非空子集,并输出每个集合的所有子集
void f(int n)
{
    for(int i=1;i<(1<<n);i++)           // 枚举所有非空集合 i(共 2^n 个)
    {
        for(int j=i;j;j=(j-1)&i)        // 枚举 i 的所有非空子集 j
        {
            cout << j << " ";          // 输出子集 j(是二进制状态)
        }
        cout << endl;
    }
}
枚举超集的代码
void f(int n)
{
    for(int i=1; i<(1<<n); i++)  // 枚举所有非空子集 i(从1到2^n-1)
    {
        for(int j=i; j; j=(j+1) | i)  // 从 j=i 开始,不断生成包含 i 的更大集合 j
        {
            if(j == (1<<n)-1 ) break;  // 如果已经到全集(所有位为1),就退出
            cout << j << " ";  // 输出 j 的整数表示
        }
        cout << endl;  // 每个 i 对应一行输出
    }
}

习题

P1469 找筷子

801. 二进制中1的个数

牛牛的xor