Description
21 世纪,许多人得了一种奇怪的病:起床困难综合症,其临床表现为:起床难,起床后精神不佳。作为一名青春阳光好少年, atm 一直坚持与起床困难综合症作斗争。通过研究相关文献,他找到了该病的发病原因: 在深邃的太平洋海底中,出现了一条名为 drd 的巨龙,它掌握着睡眠之精髓,能随意延长大家的睡眠时间。 正是由于 drd 的活动,起床困难综合症愈演愈烈, 以惊人的速度在世界上传播。为了彻底消灭这种病,atm 决定前往海底,消灭这条恶龙。
历经千辛万苦,atm 终于来到了 drd 所在的地方,准备与其展开艰苦卓绝的战斗。 drd 有着十分特殊的技能,他的防御战线能够使用一定的运算来改变他受到的伤害。具体说来,drd 的防御战线由 n 扇防御门组成。每扇防御门包括一个运算 op 和一个参数 t,其中运算一定是 OR,XOR,AND 中的一种, 参数则一定为非负整数。如果还未通过防御门时攻击力为 x ,则其通过这扇防御门后攻击力将变为 x op t 。最终 drd 受到的伤害为对方初始攻击力 x 依次经过所有 n 扇防御门后转变得到的攻击力。
由于 atm 水平有限, 他的初始攻击力只能为 0 到 m 之间的一个整数 (即他的初始攻击力只能在 0, 1, … , m 中任选,但在通过防御门之后的攻击力不受 m 的限制) 。为了节省体力,他希望通过选择合适的初始攻击力使得他的攻击能让 drd 受到最大的伤害,请你帮他计算一下,他的一次攻击最多能使 drd 受到多少伤害。
Solution
首先三个运算都是位运算,每一位的贡献都是独立的,所以可以拆位考虑。
先不考虑m的限制,答案最多有31二进制位,然后看看每一位选0还是1,时间很优。
这时多出来一个m,我们可以发现,答案最大肯定是把1放到二进制高位上更优(你可以把答案化成二进制,显然1尽量往左边放)。还有,如果一个1放在从左往右数第
i
位,那么第
于是,我们先放一个1,使它在m限制内。然后从这位开始,如果不放1的贡献不小于放1的贡献,那么这位就不放,然后往后做下去即可。
Code
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#define fo(i,j,k) for(int i=j;i<=k;i++)
#define fd(i,j,k) for(int i=j;i>=k;i--)
#define N 100010
using namespace std;
int a[N][32],b[N],c[N],n;
int _2[31];
int calc(int x,int o){
fo(i,1,n)
{
int t=a[i][o];
if(b[i]==1) x&=t;
else if(b[i]==2) x|=t;
else x^=t;
}
return x;
}
int main()
{
freopen("sleep.in","r",stdin);
freopen("sleep.out","w",stdout);
_2[0]=1;
fo(i,1,30) _2[i]=_2[i-1]*2;
int m;
scanf("%d %d",&n,&m);
fo(i,1,n)
{
char ch[10];
int x;
scanf("%s %d",ch,&x);
if(ch[0]=='A') b[i]=1;
else if(ch[0]=='O') b[i]=2;
else b[i]=3;
c[i]=-1;
while(x) a[i][++c[i]]=x%2,x/=2;
}
int t=0,tot=0;
int ans=0;
fd(i,30,0)
{
if(_2[i]<=m) {t=i;break;}
if(calc(0,i)) ans+=_2[i];
}
fd(i,t,0)
{
int pl=calc(1,i),em=calc(0,i);
if(em) {ans+=_2[i];continue;}
if(pl && tot+_2[i]<=m) ans+=_2[i],tot+=_2[i];
}
printf("%d",ans);
}