洛谷P2114 [NOI2014] 起床困难综合症 题解
题意:
cxy 的防御战线由 \(n\) 扇防御门组成。每扇防御门包括一个运算 \(\mathrm{op}\) 和一个参数 \(t\),其中运算一定是 \(\text{OR},\text{XOR},\text{AND}\) 中的一种,参数则一定为非负整数。如果还未通过防御门时攻击力为 \(x\),则其通过这扇防御门后攻击力将变为 \(x~\mathrm{op}~t\)。最终 cxy 受到的伤害为对方初始攻击力 \(x\) 依次经过所有 \(n\) 扇防御门后转变得到的攻击力。
由于 atm 水平有限,他的初始攻击力只能为 \(0\) 到 \(m\) 之间的一个整数(即他的初始攻击力只能在 \(0,1,\ldots,m\) 中任选,但在通过防御门之后的攻击力不受 \(m\) 的限制)。为了节省体力,他希望通过选择合适的初始攻击力使得他的攻击能让 cxy 受到最大的伤害,请你帮他计算一下,他的一次攻击最多能使 cxy 受到多少伤害。
输入格式:
输入文件的第 \(1\) 行包含 \(2\) 个整数,依次为 \(n, m\),表示 cxy 有 \(n\) 扇防御门,atm 的初始攻击力为 \(0\) 到 \(m\) 之间的整数。
接下来 \(n\) 行,依次表示每一扇防御门。每行包括一个字符串 \(\mathrm{op}\) 和一个非负整数 \(t\),两者由一个空格隔开,且 \(\mathrm{op}\) 在前,\(t\) 在后,\(\mathrm{op}\) 表示该防御门所对应的操作,\(t\) 表示对应的参数。
输出格式:
输出一行一个整数,表示 atm 的一次攻击最多使 cxy 受到多少伤害。
数据范围:
\(2 \le n \le 10^5,~2 \le m \le 10^9\) 。
据说是经典贪心题,赶紧来做一做。
我们可以依次考虑每一位为 \(0/1\) 时,经过所有防御门后会变成什么
然后贪心地从高向低依次枚举每一位选什么会变成 \(1\) 。
注意如果选了 \(1\) ,要把
m -= (1ll<<j)
,毕竟不能无限选嘛。
但是依次考虑每一位肯定是很慢的,因此考虑将这些位压成一个 \(\mathtt{int}\) (然而我开了 \(\mathtt{long long}\) )
时间复杂度 \(\mathcal{O}(n)\)
代码:
#include <bits/stdc++.h>
using namespace std;
#define int long long
#define INF 0x3f3f3f3f3f3f3f3f
void up(int &x,int y) { x < y ? x = y : 0; }
void down(int &x,int y) { x > y ? x = y : 0; }
#define N ((int)())
char ch[5];
int n,m,x,a1,a2=-1,res;
signed main()
{
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
// freopen("check.in","r",stdin);
// freopen("check.out","w",stdout);
cin >> n >> m;
for(int i=1; i<=n; i++)
{
cin >> ch >> x;
if(ch[0] == 'A') a1 &= x, a2 &= x;
if(ch[0] == 'X') a1 ^= x, a2 ^= x;
if(ch[0] == 'O') a1 |= x, a2 |= x;
}
for(int j=31,r; ~j; j--)
{
r = (1ll << j);
if((a1 >> j) & 1){ res += r; continue; }
if(((a2 >> j) & 1) && r <= m) res += r, m -= r;
}
cout << res << '\n';
return 0;
}
参考文献: