题目描述
21 21 21 世纪,许多人得了一种奇怪的病:起床困难综合症,其临床表现为:起床难,起床后精神不佳。
作为一名青春阳光好少年,atm 一直坚持与起床困难综合症作斗争。
通过研究相关文献,他找到了该病的发病原因: 在深邃的太平洋海底中,出现了一条名为 drd 的巨龙,它掌握着睡眠之精髓,能随意延长大家的睡眠时间。
正是由于 drd 的活动,起床困难综合症愈演愈烈, 以惊人的速度在世界上传播。
为了彻底消灭这种病,atm 决定前往海底,消灭这条恶龙。
历经千辛万苦,atm 终于来到了 drd 所在的地方,准备与其展开艰苦卓绝的战斗。
drd 有着十分特殊的技能,他的防御战线能够使用一定的运算来改变他受到的伤害。
具体说来,drd 的防御战线由 n n n 扇防御门组成。
每扇防御门包括一个运算 o p op op 和一个参数 t t t,其中运算一定是 O R , X O R , A N D OR,XOR,AND OR,XOR,AND 中的一种,参数则一定为非负整数。
如果还未通过防御门时攻击力为 x x x ,则其通过这扇防御门后攻击力将变为 x o p t x op t xopt。
最终 drd 受到的伤害为对方初始攻击力 x x x 依次经过所有 n n n 扇防御门后转变得到的攻击力。
由于 atm 水平有限,他的初始攻击力只能为 0 0 0 到 m m m 之间的一个整数(即他的初始攻击力只能在 0 , 1 , … , m 0,1,…,m 0,1,…,m 中任选,但在通过防御门之后的攻击力不受 m m m 的限制)。
为了节省体力,他希望通过选择合适的初始攻击力使得他的攻击能让 drd 受到最大的伤害,请你帮他计算一下,他的一次攻击最多能使 drd 受到多少伤害。
输入格式
第 1 1 1 行包含 2 2 2 个整数,依次为 n , m n,m n,m,表示 drd 有 n n n 扇防御门,atm 的初始攻击力为 0 0 0 到 m m m 之间的整数。
接下来 n n n 行,依次表示每一扇防御门。每行包括一个字符串 o p op op 和一个非负整数 t t t,两者由一个空格隔开,且 o p op op 在前, t t t 在后, o p op op 表示该防御门所对应的操作, t t t 表示对应的参数。
输出格式
输出一个整数,表示 atm 的一次攻击最多使 drd 受到多少伤害。
数据范围
输入样例
3 10
AND 5
OR 6
XOR 7
输出样例
1
样例解释
atm可以选择的初始攻击力为 0 , 1 , … , 10 0,1,…,10 0,1,…,10。
假设初始攻击力为 4 4 4 ,最终攻击力经过了如下计算
4 AND 5 = 4
4 OR 6 = 6
6 XOR 7 = 1
类似的,我们可以计算出初始攻击力为 1 , 3 , 5 , 7 , 9 1,3,5,7,9 1,3,5,7,9 时最终攻击力为 0 0 0,初始攻击力为 0 , 2 , 4 , 6 , 8 , 10 0,2,4,6,8,10 0,2,4,6,8,10 时最终攻击力为 1 1 1,因此 atm 的一次攻击最多使 drd 受到的伤害值为 1 1 1。
题目分析
容易发现位运算的一个特点:某一位经过运算后的结果不会影响其它位,也即参与位运算的各位之间是没有关系的。因此,我们可以从高位枚举到低位,依次考虑每一位应该填 0 0 0 还是填 1 1 1。
某一位上填 1 1 1,当且仅当满足以下两个条件:
- 该二进制数不会超过 m m m;
- 这一位填 1 1 1 经过 n n n 次位运算后结果为 1 1 1,填 0 0 0 经过位运算后结果为 0 0 0。
否则,这一位填 1 1 1 不如填 0 0 0 优。确定好最初攻击力的每一位后,自然可以得到最终的攻击力。
代码:
#include <iostream>
#include <string>
using namespace std;
const int N = 100010;
int n, m, door[N][2], ans, val;
string op;
int calc(int x, int y){
//x表示第x位,y是将要参与运算的这一位的值
for (int i = 1; i <= n; i ++){
int cur = (door[i][0] >> x) & 1;
if (door[i][1] == 1) y &= cur;
else if (door[i][1] == 2) y |= cur;
else y ^= cur;
}
return y;
}
int main(){
cin >> n >> m;
for (int i = 1; i <= n; i ++){
cin >> op >> door[i][0];
if (op == "AND") door[i][1] = 1;
else if (op == "OR") door[i][1] = 2;
else door[i][1] = 3;
}
for (int i = 29; i >= 0; i --){
//按位枚举
int x = calc(i, 1), y = calc(i, 0);
if (val + (1 << i) <= m && x > y) //如果不超过范围,且填1结果为1,填0结果为0,则这一位填1
val += 1 << i, ans += x << i;
else ans += y << i; //否则填1没有填0优,这一位填0
}
cout << ans;
return 0;
}