1. 程式人生 > >BZOJ3105:[CQOI2013]新Nim遊戲(線性基,貪心)

BZOJ3105:[CQOI2013]新Nim遊戲(線性基,貪心)

pri 如果 span std string 機會 content NPU sort

Description

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

Input

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

Output

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

Sample Input

6
5 5 6 6 5 5

Sample Output

21

HINT

k<=100

Solution

我們先手要做到把集合拿到不能異或出$0$為止,把所有數排序一下從大到小往線性基裏插,如果插入失敗的話就說明這個數能和線性基裏面的數異或出$0$,必須取走。

Code

 1 #include<iostream>
 2 #include<cstring>
 3
#include<cstdio> 4 #include<algorithm> 5 #define N (109) 6 #define LL long long 7 using namespace std; 8 9 int n,a[N],d[31]; 10 LL ans; 11 12 inline int read() 13 { 14 int x=0,w=1; char c=getchar(); 15 while (c<0 || c>9) {if (c==-) w=-1; c=getchar();}
16 while (c>=0 && c<=9) x=x*10+c-0, c=getchar(); 17 return x*w; 18 } 19 20 bool Insert(int x) 21 { 22 for (int i=30; i>=0; --i) 23 if (x&(1<<i)) 24 { 25 if (!d[i]) {d[i]=x; break;} 26 x^=d[i]; 27 } 28 return x; 29 } 30 31 int main() 32 { 33 n=read(); 34 for (int i=1; i<=n; ++i) a[i]=read(); 35 sort(a+1,a+n+1); 36 for (int i=n; i>=1; --i) 37 if (!Insert(a[i])) ans+=a[i]; 38 printf("%lld\n",ans); 39 }

BZOJ3105:[CQOI2013]新Nim遊戲(線性基,貪心)