起床困难综合征 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 ",ans); return 0; }
小结:说起来挺麻烦但是想起来贼简单,水到渠成。