[bzoj3668][Noi2014]起床困难综合症_暴力

起床困难综合征 bzoj-3668 Noi-2014

题目大意题目链接

注释:略。


想法:Noi考这题...联赛T1难度....

我们将每个门上的数二进制拆分。

发现:当前位的操作可能直接确定了当前位的数字。

即:如果当前位上是0,操作是&,那么这一位无论开始的数是多少,都会变成1。同理如果当前位是1,操作是|,那么这位一定会变成1。

然而如果当前操作和位上的数不能直接确定当前的数,那么我们发现当前的数一定是由初始给定的数确定的。

也就是说我们维护一个数,这个数二进制拆分后,每一位可能是0、1、x和x'。如果是0或1就表示当前位已经被完全确定了。

如果是x表示当前位和初始位相等,x'表示相反。

把所有的门都枚举完了之后,我们看最终的数。

从高位往低位枚举,如果是0或1,那自然不必说。

如果是x',就让初始值这位是0即可,答案显然优

如果是x,判断一下当前剩下的值有没有把这位变成1大。如果有,就变,并将当前值减去当前位,反之枚举下一位。

最后,附上丑陋的代码... ...

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define N 100010 
#define M 35 
using namespace std;
int s[N][M]; char opt[N][10];
int a[N],mdl[M];
inline void dvd(int x,int idx)
{
	while(x)
	{
		s[idx][++s[idx][0]]=x&1;
		x>>=1;
	}
}
int main()
{
	int n,m; cin >> n >> m ; for(int i=1;i<=n;i++)
	{
		scanf("%s%d",opt[i]+1,&a[i]);
		dvd(a[i],i);
	}
	for(int i=1;i<=30;i++) mdl[i]=3;
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=30;j++)
		{
			if(opt[i][1]=='A') if(!s[i][j]) mdl[j]=0;
			else if(opt[i][1]=='O') if(s[i][j]) mdl[j]=1;
			else
			{
				if(s[i][j])
				{
					if(mdl[j]==4) mdl[j]=3;
					else if(mdl[j]==3) mdl[j]=4;
					else if(mdl[j]==1) mdl[j]=0;
					else mdl[j]=1;
				}
			}
		}
	}
	int ans=0;
	for(int i=30;i;i--)
	{
		if(mdl[i]==3)
		{
			if(m>=(1<<(i-1)))
			{
				m-=(1<<(i-1));
				mdl[i]=1;
			}
			else mdl[i]=0;
		}
		else if(mdl[i]==4) mdl[i]=1;
	}
	for(int i=30;i>=1;i--)
	{
		ans+=mdl[i]*(1<<(i-1));
	}
	printf("%d\n",ans);
	return 0;
}

小结:说起来挺麻烦但是想起来贼简单,水到渠成。

posted @ 2018-09-24 20:51  JZYshuraK_彧  阅读(215)  评论(0编辑  收藏  举报