P2114 [NOI2014]起床困难综合症

题目地址

经典状态压缩题,思路挺巧妙的。(也可能是我太蒻了吧)、、、

题解

我们要从 \([0-m]\) 中间选一个数 \(x_0\) 使得最后的攻击力最大。

如果从二进制的角度来观察 \(x_0\) ,你就会发现一系列的 \(\text{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\n",ans);
    return 0;
}

刚刚看了一下洛谷题解,发现题解的大佬们都吊打我,还说这是一道大水题。

全部评论

相关推荐

美团 后端开发 总包n(15%是股票)
点赞 评论 收藏
分享
评论
点赞
收藏
分享
牛客网
牛客企业服务