位运算
位运算
Part1. 基础运算
& 与 两个位都为1时,结果才为1
| 或 两个位都为0时,结果才为0
^ 异或 两个位相同为0,相异为1
~ 取反 0变1,1变0
<< 左移 各二进位全部左移若干位,高位丢弃,低位补0
>> 右移 各二进位全部右移若干位,对无符号数,高位补0,有符号数,各编译器处理方法不一样,有的补符号位(算术右移),有的补0(逻辑右移)
Part2. 混合运算
(n >> k) & 1 取 n 的第 k 位
n & ((1 << k) - 1) 取 n 的后 k 位
n ^ (1 << k) 将 n 的第 k 位取反
n | (1 << k) 将 n 的第 k 位改为 1
n & (~(1 << k)) 将 n 的第 k 位改为 0
Part3. 其他函数
int __builtin_popcount(unsigned int x)
:返回 x
的二进制中 1
的个数。
int __builtin_ffs(int x)
:返回 x 的二进制末尾最后一个 1
的位置,位置的编号从 1
开始(最低位编号为 1
)。当 x
为 0
时返回 0
。
int __builtin_clz(unsigned int x)
:返回 x
的二进制的前导 0
的个数。当 x
为 0
时,结果未定义。
int __builtin_ctz(unsigned int x)
:返回 x
的二进制末尾连续 0
的个数。当 x
为 0
时,结果未定义。
Part4. 例题
[NOI2014] 起床困难综合症
我们发现,直接找最大值必定 T 飞,考虑使用二进制。
那么对于这个题,二进制有点在于什么地方呢?
很显然,我们在十进制中,想让一个数最大,是不是先判断最高位,最高位最大就一定最大。在二进制中也是如此。而且二进制一个位上就两种可能,要么是一要么是零。
我们可以随便搞两个变量记录二进制全 \(0\) 和全 \(1\) 经过门之后的样子。
然后从最高位开始找就可以了。
注释代码:
#include<bits/stdc++.h>
#define rint register int
#define endl '\n'
#define int long long
using namespace std;
const int N = 1e5 + 5;
int n, m;
int bit1 = 0, bit2 = -1;
//这两个数用来预处理,一个是全零,一个是全一
int ans;
signed main()
{
cin >> n >> m;
for (rint i = 1; i <= n; i++)
{
string s;
int a;
cin >> s >> a;
if (s[0] == 'A')
{
bit1 &= a;
bit2 &= a;
}
if (s[0] == 'O')
{
bit1 |= a;
bit2 |= a;
}
if (s[0] == 'X')
{
bit1 ^= a;
bit2 ^= a;
}
}
//从最高位开始枚举
for (rint i = 32; i >= 0; i--)
{
if ((bit1 >> i) & 1)
/*
如果对于当前位而言
bit1 出来为 1
累计答案
*/
{
ans += (1ll << i);
}
else if ((bit2 >> i) & 1)
{
if((1ll << i) <= m)
{
m -= 1ll << i;
ans += 1ll << i;
}
}
}
cout << ans << endl;
return 0;
}