【JZOJ3753】【NOI2014】起床困难综合症

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 位,那么第i+1位后不管放多少个1贡献都没有放在第 i <script type="math/tex" id="MathJax-Element-167">i</script>位多。

于是,我们先放一个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);
}
posted @ 2017-04-19 20:14  sadstone  阅读(56)  评论(0编辑  收藏  举报