1. 程式人生 > >BZOJ_3105_[cqoi2013]新Nim遊戲_線性基+博弈論

BZOJ_3105_[cqoi2013]新Nim遊戲_線性基+博弈論

不存在 不可 con 傳統 規則 output bzoj brush int

BZOJ_3105_[cqoi2013]新Nim遊戲_線性基+博弈論

Description

傳統的Nim遊戲是這樣的:有一些火柴堆,每堆都有若幹根火柴(不同堆的火柴數量可以不同)。兩個遊戲者輪流操作,每次可以選一個火柴堆拿走若幹根火柴。可以只拿一根,也可以拿走整堆火柴,但不能同時從超過一堆火柴中拿。拿走最後一根火柴的遊戲者勝利。 本題的遊戲稍微有些不同:在第一個回合中,第一個遊戲者可以直接拿走若幹個整堆的火柴。可以一堆都不拿,但不可以全部拿走。第二回合也一樣,第二個遊戲者也有這樣一次機會。從第三個回合(又輪到第一個遊戲者)開始,規則和Nim遊戲一樣。 如果你先拿,怎樣才能保證獲勝?如果可以獲勝的話,還要讓第一回合拿的火柴總數盡量小。

Input

第一行為整數k。即火柴堆數。第二行包含k個不超過109的正整數,即各堆的火柴個數。

Output

輸出第一回合拿的火柴數目的最小值。如果不能保證取勝,輸出-1。

Sample Input

6

5 5 6 6 5 5

Sample Output

21

HINT

k<=100


傳統Nim遊戲先手必勝當且僅當石子異或和不為0.

也就是說後手要盡可能選擇一些數,是剩下的異或和為0.

轉化為這樣一個問題,刪去盡可能少的石子,使得剩下的不存在一種方案使拿出的異或和為0。

即選擇盡可能多的石子使他們線性不相關。

線性基貪心即可。

代碼:

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long ll;
int b[50],a[150],n;
ll sum;
bool cmp(int x,int y) {return x>y;}
bool insert(int x) {
	int i;
	for(i=30;i>=0;i--) {
		if(x&(1<<i)) {
			if(b[i]) x^=b[i];
			else {
				b[i]=x; return 1;
			}
		}
	}
	return 0;
}
int main() {
	scanf("%d",&n);
	int i;
	for(i=1;i<=n;i++) scanf("%d",&a[i]),sum+=a[i];
	sort(a+1,a+n+1,cmp);
	for(i=1;i<=n;i++) {
		if(insert(a[i])) sum-=a[i];
	}
	printf("%lld\n",sum);
}

  

BZOJ_3105_[cqoi2013]新Nim遊戲_線性基+博弈論