[BZOJ3668][Noi2014]起床困难综合症(贪心)

本文介绍了一种解决特定类型位运算问题的方法。通过将原始数值x按二进制位拆分,并针对每个二进制位独立应用一系列逻辑操作,如AND、OR、XOR等,以确定最优解。该方法通过比较不同位操作后的结果来决定x的每一位是0还是1,最终组合成完整的x值,并确保结果不超过给定限制m。

题目描述

传送门

题解

我们需要还原初始的x,将x按二进制位分开来考虑
每一位不是0就是1,所以将0和1分别做一下下面那一坨操作(操作的数也是对应的这一位),最终得到两个数
如果0做了这一坨操作之后变成了1,那很显然这一位填0更优
否则,如果1做了这一坨操作之后还是1,那么先把这一位暂且记为1
再否则1和0都会变成0,那么毫无疑问填0
然后将每一位合起来得到了一个x
但是这个x是有可能大于m的,所以从高位向低位枚举,贪心地去掉一些1,
如果这一位m是1,那么x填01都可以,不变;不过,如果x填了0的话,x之后的位就可以随便填了
如果这一位m是0,那么x只能填0,如果原先填的是1需要修改
这样保证先满足较高位的,一定是最优的方案

代码

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
using namespace std;
#define N 100005
#define sz 30

char s[10];
int n,m,t,ans0,ans1,ans;
int opt[N],dig[N][sz],mdig[sz];
bool flag;

int main()
{
    scanf("%d%d",&n,&m);
    for (int i=sz-1;i>=0;--i) mdig[i]=m>>i&1;
    for (int i=1;i<=n;++i)
    {
        scanf("%s",s);
        if (s[0]=='A') opt[i]=1;
        else if (s[0]=='O') opt[i]=2;
        else opt[i]=3;
        scanf("%d",&t);
        for (int j=sz-1;j>=0;--j) dig[i][j]=t>>j&1;
    }
    flag=0;
    for (int j=sz-1;j>=0;--j)
    {
        ans0=0;ans1=1;
        for (int i=1;i<=n;++i)
        {
            switch(opt[i])
            {
                case 1:
                    {
                        ans0&=dig[i][j];
                        ans1&=dig[i][j];
                        break;
                    }
                case 2:
                    {
                        ans0|=dig[i][j];
                        ans1|=dig[i][j];
                        break;
                    }
                case 3:
                    {
                        ans0^=dig[i][j];
                        ans1^=dig[i][j];
                        break;
                    }
            }
        }
        if (ans0)
        {
            ans|=1<<j;
            if (mdig[j]) flag=1;
        }
        else if(ans1){if(mdig[j]||flag)ans|=1<<j;}
        else {if(mdig[j])flag=1;}
    }
    printf("%d\n",ans);
}

总结

位运算的题目一定要考虑按位分开,这一点很重要

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值