1. 程式人生 > >洛谷P4301 [CQOI2013]新Nim遊戲

洛谷P4301 [CQOI2013]新Nim遊戲

洛谷P4301 [CQOI2013]新Nim遊戲

題目描述

傳統的Nim遊戲是這樣的:有一些火柴堆,每堆都有若干根火柴(不同堆的火柴數量可以不同)。兩個遊戲者輪流操作,每次可以選一個火柴堆拿走若干根火柴。可以只拿一根,也可以拿走整堆火柴,但不能同時從超過一堆火柴中拿。拿走最後一根火柴的遊戲者勝利。

本題的遊戲稍微有些不同:在第一個回合中,第一個遊戲者可以直接拿走若干個整堆的火柴。可以一堆都不拿,但不可以全部拿走。第二回合也一樣,第二個遊戲者也有這樣一次機會。從第三個回合(又輪到第一個遊戲者)開始,規則和Nim遊戲一樣。

如果你先拿,怎樣才能保證獲勝?如果可以獲勝的話,還要讓第一回合拿的火柴總數儘量小。

輸入輸出格式

輸入格式:

第一行為整數\(k\)。即火柴堆數。
第二行包含\(k\)個不超過10^9的正整數,即各堆的火柴個數。

輸出格式:

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

思路:

線性基
對於nim遊戲,有一個規律就是如果所有數異或和不為零,那麼就是先手勝,否則先手敗。
可以這麼理解這個規律,如果異或和不為零,那麼說明此時棋盤上還有棋子,那麼先手一定可以通過一次操作使得異或和變成零。
當異或和變成零之後,就會有兩種情況,一種是棋盤上還有棋子,一種是棋盤上沒有棋子。
對於第二種情況,已經獲得勝利了。
而對於第一種情況,後手的一次操作一定會讓異或和非零,那麼就再次回到了一開始的情況,如此重複,先手一定會贏。

那麼對於新nim遊戲,留給先手的問題就變成了構造出一個和最大的數集,使這個數集中全部或部分的數以任意方式組合進行異或運算都無法使得異或和為零,即後手無法構造出異或和為零的狀態(他的必勝狀態)。

至此就符合了線性基性質。

CODE:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
#define MAXN 110
int nums[MAXN],li[MAXN];
int i,j,k,m,n;
long long int ans;
bool insert(int n){
    for(int i=62;i>=0;i--){
        if((n>>i)&1){
            if(!li[i]){
                li[i]=n;
                return true;
            }
            n^=li[i];
        }
    }
    return false;
}
int main(){
    scanf("%d",&n);
    for(i=1;i<=n;i++) scanf("%d",nums+i);
    sort(nums+1,nums+n+1);
    for(i=n;i>=1;i--){
        if(!insert(nums[i])) ans+=nums[i];
    }
    printf("%lld\n",ans);
    return 0;
}