题目地址
经典状态压缩题,思路挺巧妙的。(也可能是我太蒻了吧)、、、
题解
我们要从 ([0-m]) 中间选一个数 (x_0) 使得最后的攻击力最大。
如果从二进制的角度来观察 (x_0) ,你就会发现一系列的 ( ext{xor})、 (&) 、 (|) 操作只不过是 (x_0) 二进制下的每一位在进行这一系列操作,使得最后的答案最大。
“换言之,对于任意的 k(0<=k<=30),‘ans 的第 k 位是几’只与‘ (x_0) 的第 k 位是几’有关”
于是考虑二进制下填位的方法,从高位到低位依次填 (1) 或 (0)
(x_0) 的 (k) 位上填 1 ,当且仅当
-
(x_0) 的 (k) 位上填 1 ,值不会超过 (m)
-
(x_0) 的 (k) 位上填 1 比填 0 要优
知道什么时候太填 (1) 什么时候填 (0),你就可以轻轻松松切掉这道题了。
Code
#include<bits/stdc++.h>
using namespace std;
const int N = 1e5+7;
int n,m;
int t[N];
string op[N];
inline int calc(int k,int now) {
for(int i=1;i<=n;++i) {
int x = (t[i]>>k) & 1;
if(op[i] == "AND") now &= x;
else if(op[i] == "OR") now |= x;
else now ^= x;
}
return now;
} //运算n次后看返回 1 还是 0
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;++i) {
char str[5];
scanf("%s%d",str,&t[i]); op[i] = str;
}
int val = 0 ,ans = 0; //ans来保存最后攻击力
for(int i=29;i>=0;--i) { //位运算学着学着就从零开始计数了qwq
int res0 = calc(i,0) ,res1 = calc(i,1); //分别处理看一下填0和填一会怎么样
if(val+(1<<i)<=m && res0<res1)
val += (1<<i) ,ans += (res1<<i);
else
ans += (res0<<i);
}
printf("%d
",ans);
return 0;
}
刚刚看了一下洛谷题解,发现题解的大佬们都吊打我,还说这是一道大水题。