Luogu P2114 [NOI2014]起床困难综合症|位运算

题目链接

题意:在$0$~$m$的范围内选一个数,使其通过$n$扇防御门(位运算)后攻击力最高,求最后攻击力最高为多少。

首先感谢LYD大佬的讲解~

言归正传,假设我们去除了$m$的限制,我们怎么做呢?

我们知道位运算有一个很重要的性质:不进位

也就是说,我们在每一个位上的取值与其他位无关。

所以,我们可以暴力枚举每一位上是取$0$还是$1$,由于位数不会太大,所以我们可以通过。

现在,我们再看回$m$的限制

显然,我们可以在上面的基础上,再加上贪心,从高到低位枚举,如果选这一位会超过$m$或本身选1不会比选0更优,则该位选0,否则选1。

请自己证明上面做法的正确性

上代码

#include<bits/stdc++.h>
using namespace std;
string st;int p,c[100100],cp[100100],max,n,m,man,ans;
int main()
{
    cin>>n>>m;
    for (int i=1;i<=n;i++)
    {
        cin>>st>>p;
        if (st=="AND") c[i]=1;
        if (st=="OR") c[i]=2;
        if (st=="XOR") c[i]=3;//改字符串为数字,更好操作
        cp[i]=p;
    }
    for (int i=30;i>=0;i--)//逐位枚举
    {
        int x0=0,x1=1;
        for (int j=1;j<=n;j++)
        {
            if (c[j]==1) x0&=(cp[j]>>i)&1,x1&=(cp[j]>>i)&1;
            if (c[j]==2) x0|=(cp[j]>>i)&1,x1|=(cp[j]>>i)&1;
            if (c[j]==3) x0^=(cp[j]>>i)&1,x1^=(cp[j]>>i)&1; 
        }//模拟选0与选1经过防御门后的最终值
        if (x0>=x1||((man|(1<<i))>m)) 
        {
            ans|=(x0<<i);
        }
        else
        {
            man|=(1<<i);
            ans|=(x1<<i);
        } 
    }
    cout<<ans<<endl;
    return 0;
} 

事实上,上面枚举“过防御门”的做法其实可以做一些微小的优化,即用两个变量分别为$0$与$2^{30}-1$,再枚举“过防御门”,这样只用枚举一次“过防御门”了。

猜你喜欢

转载自www.cnblogs.com/fmj123/p/luogu2114.html
今日推荐