BZOJ_3105_[cqoi2013]新Nim遊戲_線性基+博弈論
阿新 • • 發佈:2018-06-15
不存在 不可 con 傳統 規則 output bzoj brush int
第一行為整數k。即火柴堆數。第二行包含k個不超過109的正整數,即各堆的火柴個數。
5 5 6 6 5 5
BZOJ_3105_[cqoi2013]新Nim遊戲_線性基+博弈論
Description
傳統的Nim遊戲是這樣的:有一些火柴堆,每堆都有若幹根火柴(不同堆的火柴數量可以不同)。兩個遊戲者輪流操作,每次可以選一個火柴堆拿走若幹根火柴。可以只拿一根,也可以拿走整堆火柴,但不能同時從超過一堆火柴中拿。拿走最後一根火柴的遊戲者勝利。 本題的遊戲稍微有些不同:在第一個回合中,第一個遊戲者可以直接拿走若幹個整堆的火柴。可以一堆都不拿,但不可以全部拿走。第二回合也一樣,第二個遊戲者也有這樣一次機會。從第三個回合(又輪到第一個遊戲者)開始,規則和Nim遊戲一樣。 如果你先拿,怎樣才能保證獲勝?如果可以獲勝的話,還要讓第一回合拿的火柴總數盡量小。Input
Output
輸出第一回合拿的火柴數目的最小值。如果不能保證取勝,輸出-1。Sample Input
65 5 6 6 5 5
Sample Output
21HINT
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遊戲_線性基+博弈論