洛谷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;
}
参考文献: