1. 程式人生 > 實用技巧 >P2114 [NOI2014]起床困難綜合症

P2114 [NOI2014]起床困難綜合症

題目描述

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表示對應的引數。

輸出格式

輸出一行一個整數,表示atm的一次攻擊最多使drd受到多少傷害。

輸入輸出樣例

輸入 #1

3 10
AND 5
OR 6
XOR 7

輸出 #1

1

說明/提示

【樣例說明】

atm可以選擇的初始攻擊力為 0,1, … ,10。

假設初始攻擊力為 4,最終攻擊力經過了如下計算

4 AND 5 = 4

4 OR 6 = 6

6 XOR 7 = 1

類似的,我們可以計算出初始攻擊力為 1,3,5,7,9 時最終攻擊力為 0,初始攻擊力為 0,2,4,6,8,10 時最終攻擊力為 1,因此atm的一次攻擊最多使drd受到的傷害值為1。

【資料規模與約定】

首先,我們可以貪心,答案要儘可能最高位要是1。

那我們原來得數這一位為1的話,要滿足兩個條件

  1. 從這一位開始,後面都填0的話要小於m。

2.填1的得到的結果要比填0要優

這樣,我們可以列舉一開始這個數的每一位。

對他的這一位分別進行運算,看填1還是填0要優

程式碼

#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
inline int read()
{
	int s = 0, w = 1; char ch = getchar();
	while(ch < '0' || ch > '9'){if(ch == '-') w = -1; ch = getchar();}
	while(ch >= '0' && ch <= '9'){s = s * 10+ch -'0'; ch = getchar();}
	return s * w;
}
string opt;
int n,m,shu,ans,t;
struct node
{
	string opt;
	int t;
}e[100010];
int calc(int wei,int val)
{
	for(int i = 1; i <= n; i++)
	{
		int x = (e[i].t>>wei) & 1;//取出每個運算元的第i位,並進行操作
		if(e[i].opt == "OR") val |= x;
		else if(e[i].opt == "AND") val &= x;
		else val ^= x;
	}
	return val;
}
int main()
{
	n = read(); m = read();
	for(int i = 1; i <= n; i++)
	{
		cin>>opt; t = read();//先把所有操作都存起來
		e[i].opt = opt;
		e[i].t = t;
	}
	for(int i = 30; i >= 0; i--)//列舉每一位
	{
		int f1 = calc(i,0);//計算填0的結果
		int f2 = calc(i,1);//計算填1的結果
		if(shu+(1<<i) <= m && f1 < f2)//shu位當前已經填好的數,如果這個數填了1以後,
                                              // 後面的位填0都要比m大,那我們就不可以填1,只能填0
		{
			shu += (1<<i);//一開始的數這一位填1更優
			ans += (f2<<i);//填1得到的結果
		}
		else shu += (0<<i), ans += (f1<<i);
	}
	printf("%d\n",ans);
	return 0;
}