【题解】AcWing 998.起床困难综合症

AcWing 998.起床困难综合症

题目描述

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,当且仅当满足以下两个条件:

  1. 该二进制数不会超过 m m m
  2. 这一位填 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;
}

Guess you like

Origin blog.csdn.net/f4u4u4r/article/details/117968326