CH0104 起床困难综合症

Neurocoda

Description

21 世纪,许多人得了一种奇怪的病:起床困难综合症,其临床表现为:起床难,起床后精神不佳。作为一名青春阳光好少年,atm 一直坚持与起床困难综合症作斗争。通过研究相关文献,他找到了该病的发病原因:在深邃的太平洋海底中,出现了一条名为 drd 的巨龙,它掌握着睡眠之精髓,能随意延长大家的睡眠时间。正是由于 drd 的活动,起床困难综合症愈演愈烈,以惊人的速度在世界上传播。为了彻底消灭这种病,atm 决定前往海底,消灭这条恶龙。

历经千辛万苦,atm 终于来到了 drd 所在的地方,准备与其展开艰苦卓绝的战斗。drd 有着十分特殊的技能,他的防御战线能够使用一定的运算来改变他受到的伤害。具体说来,drd 的防御战线由扇防御门组成。每扇防御门包括一个运算 op 和一个参数,其中运算一定是 OR,XOR,AND 中的一种,参数则一定为非负整数。如果还未通过防御门时攻击力为,则其通过这扇防御门后攻击力将变为op。最终 drd 受到的伤害为对方初始攻击力依次经过所有扇防御门后转变得到的攻击力。

由于 atm 水平有限,他的初始攻击力只能为到之间的一个整数(即他的初始攻击力只能在中任选,但在通过防御门之后的攻击力不受的限制)。为了节省体力,他希望通过选择合适的初始攻击力使得他的攻击能让 drd 受到最大的伤害,请你帮他计算一下,他的一次攻击最多能使 drd 受到多少伤害。

Input

第 1 行包含 2 个整数,依次为,,表示 drd 有扇防御门,atm 的初始攻击力为到之间的整数。

接下来行,依次表示每一扇防御门。每行包括一个字符串 op 和一个非负整数,两者由一个空格隔开,且 op 在前,在后,op 表示该防御门所对应的操作,表示对应的参数。

Output

输出一行一个整数,表示 atm 的一次攻击最多使 drd 受到多少伤害。

Sample Input

1
2
3
4
3 10
AND 5
OR 6
XOR 7

Sample Output

1
1

atm 可以选择的初始攻击力为 0,1, … ,10。
假设初始攻击力为 4,最终攻击力经过了如下计算
4 AND 5 = 4
4 OR 6 = 6
6 XOR 7 = 1
类似的,我们可以计算出初始攻击力为 1,3,5,7,9 时最终攻击力为 0,初始攻击力为 0,2,4,6,8,10 时最终攻击力为 1,因此 atm 的一次攻击最多使 drd 受到的伤害值为 1。

Limit

Time LimitMemory Limit
1 秒(C/C++/Rust/Pascal),2 秒(其他语言)512 M(C/C++/Rust/Pascal),1024 M(其他语言)

Analysis

首先,我们可以先列出计算的表达式:

可能会想尝试化简公式,尽管部分算子组合仍满足交换律、算子之间存在严格的分配律(如 & 对 | 和 ^ 具有分配律),但是混合位运算不完全满足结合律,不方便泛化为解题方案。

如果暴力地进行计算,通过一个计算得到的时间复杂度为,而枚举每个的时间复杂度为,即总时间复杂度为。可以判断只有测试点 1、2、3 可以通过。而部分显然没有顺序性,无法通过二分查找来优化为。

不过根据数据规模可以推测本题的正解的时间复杂度为

回到位运算的思考方向,对于每个,一定可以拆解为二进制数组的形式,嗯… 原来 drd 的防御墙是一排二进制串…

好了,所以这道题怎么思考呢?

以的第一个 bit 为例,可以建立一个 bit 的位运算链,能否通过链反推输入?要保证输出最大,嗯,只需要保证输出为就好了,而且输入的值的范围在,是不是感觉简单了不少?那么现在的问题是:

对于只有和的混合位运算,其输出为,如何快速计算出其输入?

嗯,没想到什么快速计算的办法,和刚才的结论一样,混合位运算不完全满足结合律,不便于快速求解输入。

但是如果按位计算的话,时间复杂度部分的枚举消失了。对于,其二进制数的位数一定小于(因为在 int 范围内),对于每个位运算链的计算的时间复杂度为,一共有最多个位运算链。总的时间复杂度为

所以我们在的限制下尝试按位计算,构造最大输出就好了,非常的简单。

要贪心构造最大的输出,可构建如下规则:

  • 从高位往低位看。
  • 记录当前选出的攻击力是否已经小于 m。
  • 如果还没有小于 m:
    • m 当前位是 0,攻击力这一位只能选 0。
    • m 当前位是 1,攻击力这一位可以选 0 或 1;选能让伤害当前位变成 1 的那个。如果两种选择的伤害位相同,就选 0,并标记攻击力已经小于 m。
  • 如果已经小于 m,后面的位就不再受 m 限制,每一位都选能让伤害位变成 1 的输入。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
#include <iostream>
#include <cstring>
#define x first
#define y second
using PSI = std::pair<std::string, int>;

const int N = 1e5 + 10;

int n, m, t, ans;
std::string op;
PSI ops[N];

template <class T>
T calc(const T& a, const std::string& op, const T& b) {
if(op == "AND") return a & b;
else if(op == "OR") return a | b;
else if(op == "XOR") return a ^ b;
std::cout << "Error\n";
return -1;
}

int breakThrough(int a, int idx) {
for(int i = 0; i < n; ++i) {
std::string op = ops[i].x;
int b = ops[i].y >> idx & 1;
a = calc<int>(a, op, b);
}
return a;
}

int main () {
std::cin >> n >> m;

for(int i = 0; i < n; ++i) {
std::cin >> op >> t;
ops[i] = {op, t};
}

bool less = 0;
for(int i = 30; i >= 0; --i) {
int input = m >> i & 1;
bool isZero = !input;
int output1 = breakThrough(input, i);
int output2 = breakThrough(!input, i);
// 有 16 种组合
if(isZero) {
if(less) {
if(output1) {
if(output2) { // [1] 前缀已小于m,低位可自由选择,实际输入为 0 情况下输出为 1,输入为 1 的情况下输出为 1
ans += 1 << i;
}
else { // [2] 前缀已小于m,低位可自由选择,实际输入为 0 情况下输出为 1,输入为 1 的情况下输出为 0
ans += 1 << i;
}
}
else {
if(output2) { // [3] 前缀已小于m,低位可自由选择,实际输入为 0 情况下输出为 0,如果输入为 1 情况下输出 1
ans += 1 << i;
}
else { // [4] 前缀已小于m,低位可自由选择,实际输入为 0 情况下输出为 0,如果输入为 1 情况下输出 0

}
}
}
else {
if(output1) {
if(output2) { // [5] 前缀仍等于 m,当前位受上界约束,实际输入为 0 情况下输出为 1,输入为 1 的情况下输出为 1
ans += 1 << i;
}
else { // [6] 前缀仍等于 m,当前位受上界约束,实际输入为 0 情况下输出为 1,输入为 1 的情况下输出为 0
ans += 1 << i;
}
}
else {
if(output2) { // [7] 前缀仍等于 m,当前位受上界约束,实际输入为 0 情况下输出为 0,如果输入为 1 情况下输出 1

}
else { // [8] 前缀仍等于 m,当前位受上界约束,实际输入为 0 情况下输出为 0,如果输入为 1 情况下输出 0

}
}
}
}
else {
if(less) {
if(output1) {
if(output2) { // [9] 前缀已小于m,低位可自由选择,实际输入为 1 情况下输出为 1,输入为 0 的情况下输出为 1
less = 1;
ans += 1 << i;
}
else { // [10] 前缀已小于m,低位可自由选择,实际输入为 1 情况下输出为 1,输入为 0 的情况下输出为 0
ans += 1 << i;
}
}
else {
if(output2) { // [11] 前缀已小于m,低位可自由选择,实际输入为 1 情况下输出为 0,如果输入为 0 情况下输出 1
ans += 1 << i;
}
else { // [12] 前缀已小于m,低位可自由选择,实际输入为 1 情况下输出为 0,如果输入为 0 情况下输出 0

}
}
}
else {
if(output1) {
if(output2) { // [13] 前缀仍等于 m,当前位受上界约束,实际输入为 1 情况下输出为 1,输入为 0 的情况下输出为 1
less = 1;
ans += 1 << i;
}
else { // [14] 前缀仍等于 m,当前位受上界约束,实际输入为 1 情况下输出为 1,输入为 0 的情况下输出为 0
ans += 1 << i;
}
}
else {
if(output2) { // [15] 前缀仍等于 m,当前位受上界约束,实际输入为 1 情况下输出为 0,如果输入为 0 情况下输出 1
less = 1;
ans += 1 << i;
}
else { // [16] 前缀仍等于 m,当前位受上界约束,实际输入为 1 情况下输出为 0,如果输入为 0 情况下输出 0
less = 1;
}
}
}
}
}

std::cout << ans;

return 0;
}

额…个条件的判断…我明天真的要起床困难了…

Ref

Nowcoder 起床困难综合症
Acwing 起床困难综合症

  • Title: CH0104 起床困难综合症
  • Author: Neurocoda
  • Created at : 2026-09-27 19:56:20
  • Updated at : 2026-09-28 00:20:14
  • Link: https://neurocoda.com/p/342125f3.html
  • License: This work is licensed under CC BY-ND 4.0.