P2114 [NOI2014] 起床困难综合症 题解
P2114 [NOI2014] 起床困难综合症 题解
简化题意
把一个范围在 \(0 \sim m\) 的数经过一系列给定的位运算操作,最大化其结果。
解题思路
首先,\(m\) 的范围很大,直接暴力枚举是不可行的。
考虑到:假如我们知道每一位是什么,在经过操作后可以为 \(1\),即对增大结果有意义,那么就可以贪心的从高位到低位选择并算出答案了。
但是我们要是用字符串之类的模拟也会很麻烦,因此这里用位运算来解决。
根据补码的知识点,我们知道 \(-1\) 在二进制下的表示就是全 \(1\) 了。因此我们分别设两个变量,一个为 \(0\)(模拟所有数位为 \(0\));一个为 \(-1\)(模拟所有数位为 \(1\))。
然后经过给定的位运算操作,我们对两个位运算所得的串都从高位到低位遍历:
- 假如由 \(0\) 变为 \(1\),就可以此位留空,不做操作(获得价值为 \(2 ^ i\),\(i\) 为当前是第几位)。
- 假如由 \(1\) 变为 \(1\),则此位所花价值为 \(2 ^ i\)(获得价值为 \(2 ^ i\)),注意需要 \(2 ^ i\) 小于剩余的 \(m\) 值。
经过这样的操作,我们就可以得到答案(答案是对于每位,加上所获得的价值)。
代码部分
#include <bits/stdc++.h>
using namespace std;
int n, m;
int a = 0, b = -1;
long long ans = 0;
// a 用来记录 第 i 位若初始为 0,则操作过后的情况
//同理,b 来记录初始为 1 操作后的情况
int main()
{
cin >> n >> m;
for(int i = 1;i <= n;i ++)
{
string op;
int t;
cin >> op >> t;
if(op == "AND")
a &= t, b &= t;
else if(op == "OR")
a |= t, b |= t;
else
a ^= t, b ^= t;
}
//我们贪心的来想:一定是越高位的地方是 1 的越多值越大。所以从高位向低位贪心。
for(int i = 30;i >= 0;i --)
{
if((a >> i) & 1)//这位置为 0 也会变成 1,所以不花价值
ans += (1 << i);
else if((b >> i) & 1 && m >= (1 << i))
ans += (1 << i), m -= (1 << i);
}
cout << ans << endl;
return 0;
}
浙公网安备 33010602011771号