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;
}

posted on 2026-07-27 15:10  Leo_29  阅读(17)  评论(0)    收藏  举报

导航