• 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$,再枚举“过防御门”,这样只用枚举一次“过防御门”了。

  • 相关阅读:
    SQL Server中使用convert进行日期转换
    杂记
    sqlserver表分区与调优与行列转换
    HttpModule的认识与深入理解及MVC运行机制
    再谈委托
    ASP.NET forms凭据设置和跳转的几种方法
    IOS学习网址
    Activator.CreateInstance 方法 (Type) 的用法
    update多表更新的2种方式
    SQL自定义函数split分隔字符串
  • 原文地址:https://www.cnblogs.com/fmj123/p/luogu2114.html
Copyright © 2020-2023  润新知