1. 程式人生 > >【BZOJ3668】[NOI2014] 起床困難綜合症(位運算思想)

【BZOJ3668】[NOI2014] 起床困難綜合症(位運算思想)

點此看題面

大致題意: 給定一些位運算操作,讓你在\(0\sim m\)範圍內選一個初始值,使其在經過這些運算後得到的結果最大。


前置技能:關於位運算

作為一道位運算的題,如果你不知道什麼是位運算,那就完全做不了了。

關於位運算可以詳見這篇部落格:位運算相關(一)——位運算學習筆記

接下來,我們還要了解位運算的一個性質:即位運算的各數位之間是互不影響的

根據這個性質,我們就可以得出一個大致思路了。


大致思路

由於位運算時各數位是相互獨立的,因此對於各數位,其實只有兩種情況:\(0\)\(1\)

則我們可以考慮用兩個變數\(s1\)\(s2\),初始化分別為\(0\)

\(-1\)\(-1\)在二進位制下每一位都是\(1\)),分別表示每一位選\(0\)和選\(1\)的情況,來執行完所有操作。

則此時,對於二進位制下的某一位,有三種情況:

  • \(s1\)中這一位為\(1\)。說明如果這一位初始值為\(0\)時最終結果為\(1\),則自然選這一位為\(0\)

  • \(s1\)中這一位為\(0\),但\(s2\)中這一位為\(1\)。說明如果這一位初始值為\(1\)時最終結果為\(0\),則判斷當前這位是否能選為\(1\),如果能選,由於二進位制數的大小是由高位決定的,因此必選,否則為\(0\)
  • \(s1\)\(s2\)中這一位全為\(0\)。則說明這一位無論如何最終結果都為\(0\)

    ,則貪心地選其為\(0\)

按照這樣的套路,就能求出答案了。


程式碼

#include<bits/stdc++.h>
#define max(x,y) ((x)>(y)?(x):(y))
#define min(x,y) ((x)<(y)?(x):(y))
#define Gmax(x,y) (x<(y)&&(x=(y)))
#define Gmin(x,y) (x>(y)&&(x=(y))) 
#define abs(x) ((x)<0?-(x):(x))
#define swap(x,y) (x^=y^=x^=y)
#define uint unsigned int
#define LL long long
#define ull unsigned long long
#define INF 1000000000
using namespace std;
int n,m,s1=0,s2=-1;//用兩個變數s1和s2分別表示每一位選0和選1的情況
class Class_FIO
{
    private:
        #define Fsize 100000
        #define tc() (A==B&&(B=(A=Fin)+fread(Fin,1,Fsize,stdin),A==B)?EOF:*A++)
        #define pc(ch) (void)(FoutSize<Fsize?Fout[FoutSize++]=ch:(fwrite(Fout,1,FoutSize,stdout),Fout[(FoutSize=0)++]=ch))
        int f,FoutSize,Top;char ch,Fin[Fsize],*A,*B,Fout[Fsize],Stack[Fsize];
    public:
        Class_FIO() {A=B=Fin;}
        inline void read(int &x) {x=0,f=1;while(!isdigit(ch=tc())) f=ch^'-'?1:-1;while(x=(x<<3)+(x<<1)+(ch&15),isdigit(ch=tc()));x*=f;}
        inline void reads(string &x) {x="";while(isspace(ch=tc()));while(x+=ch,!isspace(ch=tc())&&~ch);}
        inline void write(int x) {if(!x) return pc('0');x<0&&(pc('-'),x=-x);while(x) Stack[++Top]=x%10+48,x/=10;while(Top) pc(Stack[Top--]);}
        inline void clear() {fwrite(Fout,1,FoutSize,stdout),FoutSize=0;}
}F;
int main()
{
    register int i,x,ans=0;register string op;
    for(F.read(n),F.read(m),i=1;i<=n;++i) 
    {
        F.reads(op),F.read(x);
        switch(op[0])
        {
            case 'A':s1&=x,s2&=x;break;
            case 'O':s1|=x,s2|=x;break;
            case 'X':s1^=x,s2^=x;break;
        }
    }
    for(i=30;~i;--i) if(s1&(1<<i)) ans+=1<<i;else if(s2&(1<<i)&&(1<<i)<=m) ans+=1<<i,m-=1<<i;//分類討論求答案
    return F.write(ans),F.clear(),0;
}