起床困难综合症的详细题解(位运算)

题目内容

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受到多少伤害。

输入格式

第 1 行包含 2 个整数,依次为n, m,表示 drd 有n扇防御门,atm 的初始攻击力为0到m之间的整数。
接下来n行,依次表示每一扇防御门。每行包括一个字符串op和一个非负整数t,两者由一个空格隔开,且op在前,t在后,op表示该防御门所对应的操作,t表示对应的参数。

输入样例:

3 10
AND 5
OR 6
XOR 7

输出样例:

1

思路:

看题目要求很容易就能有思路直接暴力,复杂度是 O(n∗m),显然看题目的数据是不可能A掉的。所以对其进行简化。

例如样例的10的二进制是1010,5的二进制是101,6的二进制是110,7的二进制是111,你在进行位运算的时候是每一位的1或者0进行相应的位操作。这样你就有想法对每一个二进制位上的是1或者0进行组合,但这些组合对应的十进制必须小于m。例如,样例我取了个1100,显然这个是错误的,1001是可以符合范围的。也许你很迷惑,直接看代码。

假设一个数a的的二级制是00001,你经过a += (1<<3) ,a的二进制形式就变成了01001,继续进行a =a>>3的操作,a的二进制就变成了00001

还有不懂得地方,欢迎在评论区留言。

代码

#include<iostream>
#include<queue>
#include<sstream>
#include<algorithm>
#include<vector>
#include<cmath>
#include<map>
#define  ll long long
#include<set>
#define inf 0x3f3f3f3f
#include<cstring>
#include<cstdio>
//#define lo
using namespace std;


const int N = 1e5+10;
int n,m; 
pair<string, int> a[N];//pair是将2个数据组合成一组数据


int cal(int b, int p)//返回值是b位置放p经过运算后的值 
{
   	for(int i = 1; i <= n; i++){
       		int x = a[i].second >> b & 1;//取出t数字在b位置的二进制数。
       		if(a[i].first == "OR") p |= x;
       		else if(a[i].first == "XOR") p ^= x;
       		else p &= x; 
    	}
    	return p;
}
int main()
{
 	ios::sync_with_stdio(false);
 	cin.tie(0);
 	cin >> n >> m;
 	for(int i = 1; i <= n; i++) cin >> a[i].first >> a[i].second;
 	int ans = 0, sum = 0;//sum是记录已经配置好某些位置的二进制数,这些组合构成的十进制数的值
 	for(int i = 29; i >= 0; i--){//最高也就29位二进制。
  		int r = cal(i, 0);
  		int r1 = cal(i, 1);//r和r1来判断i位置如果放1和0后经过相应的运算后,是什么。显然有0,0 1,1 0,1 1,0
  		if(sum + (1 << i) <= m && r1 > r) sum += (1 << i),ans += r1 << i;
  		//这个语句对应的的就是0,1,如果是1,1,为了更有效,使后后面的数据选择范围多,就用r,也就是i对应的位置填0。
		//因为这个语句的条件就是选r1,对应的这点位置是1,所以sum += (1<<i),而r1的值是经过相应位运算的结果,所以ans += r1<<i。		

  		else ans += r << i; 
 	}
 	cout << ans << endl; 
 	return 0;
 }
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值