【BZOJ3105】【CQOI2013】新Nim遊戲
阿新 • • 發佈:2018-06-22
CA code main 做的 為什麽 正整數 統計 ID %d
我們按照每一堆的火柴數a_i從大到小來貪心,嘗試將它加入一個線性基中。如果成功加入,則視之為S中的元素;否則,則視為被舍棄的元素,統計入答案中。
這樣,我們就可以構造出一組權值最大的線性基。
至於為什麽貪心是正確的,不會出現舍棄權值較大的元素來讓某些權值較小的元素順利加入線性基這種決策,可能要用到擬陣證明,這個坑慢慢補吧。但是貌似如此的線性基的帶權問題,大概都可以套用貪心的方法,應該是得益於擬陣的相似證明。
Description
傳統的Nim遊戲是這樣的:有一些火柴堆,每堆都有若幹根火柴(不同堆的火柴數量可以不同)。兩個遊戲者輪流操作,每次可以選一個火柴堆拿走若幹根火柴。可以只拿一根,也可以拿走整堆火柴,但不能同時從超過一堆火柴中拿。拿走最後一根火柴的遊戲者勝利。
? 本題的遊戲稍微有些不同:在第一個回合中,第一個遊戲者可以直接拿走若幹個整堆的火柴。可以一堆都不拿,但不可以全部拿走。第二回合也一樣,第二個遊戲者也有這樣一次機會。從第三個回合(又輪到第一個遊戲者)開始,規則和Nim遊戲一樣。
如果你先拿,怎樣才能保證獲勝?如果可以獲勝的話,還要讓第一回合拿的火柴總數盡量小。
Input
? 第一行為整數\((k(k\le1000)\)。即火柴堆數。第二行包含\(k\)個不超過\(10^9\)的正整數,即各堆的火柴個數。
Output
? 輸出第一回合拿的火柴數目的最小值。如果不能保證取勝,輸出-1。
Sample Input
6
5 5 6 6 5 5
Sample Output
21
Solution
Nim遊戲先手必勝的條件是所有每一堆的數量異或和不為0。那麽我們現在所要做的,是保留一個集合S,使得這個集合的每一個子集異或和都不為0。這樣,不論對手從這個集合中刪去哪些子集,剩余的元素異或和都不為0。同時,我們要使得刪去的元素盡可能小,即選擇保留的元素盡可能大。所以,我們的目的其實是構造一組權值最大的線性基。
我們按照每一堆的火柴數a_i從大到小來貪心,嘗試將它加入一個線性基中。如果成功加入,則視之為S中的元素;否則,則視為被舍棄的元素,統計入答案中。
這樣,我們就可以構造出一組權值最大的線性基。
至於為什麽貪心是正確的,不會出現舍棄權值較大的元素來讓某些權值較小的元素順利加入線性基這種決策,可能要用到擬陣證明,這個坑慢慢補吧。但是貌似如此的線性基的帶權問題,大概都可以套用貪心的方法,應該是得益於擬陣的相似證明。
Code
#include <cstdio>
#include <algorithm>
using namespace std;
const int N=105;
int n,a[N];
long long ans;
namespace LB{
const int B=30;
int a[B];
bool insert(int x){
for(int i=B-1;i>=0;i--)
if(x&(1<<i)){
if(a[i]) x^=a[i];
else{a[i]=x;break;}
}
return x;
}
}
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%d",a+i);
sort(a+1,a+1+n);
for(int i=n;i>=1;i--)
if(!LB::insert(a[i])) ans+=a[i];
printf("%lld\n",ans);
return 0;
}
【BZOJ3105】【CQOI2013】新Nim遊戲