【bzoj3105】新Nim遊戲
Portal--> bzoj3105 新Nim遊戲
Solution
轉化一下問題
首先看一下原來的Nim遊戲,先手必勝的條件是:每堆數量的異或和不為\(0\)
所以在新的遊戲中,如果要保證自己(先手)有必勝策略的話,那必須要保證到一開始先手拿走若幹堆之後,後手無法拿走若幹堆使得剩下每堆的數量異或和為\(0\),也就是說我們要留下的應該是一個極大線性無關組
線性無關組這個的話我們可以通過線性基解決,具體的話就是如果\(insert\)完了之後這個數被變成了\(0\),那麽說明這個數和線性基裏面的數線性相關
容易註意到\(insert\)的順序會有影響,那麽現在的問題就是,應該選取哪些數作為線性基(或者說應該按照什麽順序把那堆數插到線性基裏)
先把結論擺出來:實際上只要按照從大到小的順序貪心地加就好了
為什麽可以貪心呢?
這裏需要借助一個很神奇的東西:Portal-->擬陣
? 我們設\(n\)個火柴堆的數目為集合\(S\),如果\(S\)的某個子集\(R\)不存在任何一個非空子集異或和為\(0\),那麽\(R\in I\)
接下來我們來證明一下\(M=(S,I)\)是一個擬陣
1、首先\(S\)肯定是一個有限集
2、遺傳性:設\(A\in I\),則由定義可知\(A\)不存在任何一個非空子集滿足異或和為\(0\),所以對於任意\(B \subseteq A\),\(B\)都滿足不存在任何一個非空子集異或和為\(0\)
3、交換性質:設\(,A,B\in I\),且\(|B|>|A|\),我們現在要證明\(\exists x\in B-A\)使得\(A\cup\{x\}\in I\),這裏考慮用反證法:假設對於\(\forall x\in B-A\)均有\(A\cup \{x\}\notin I\),則\(B-A\)中的元素均可以由\(A\)的某個子集的異或和表示,因此我們可以得到結論\(B\)中的所有元素均可以由\(A\)的某個子集的異或和來表示。但是這與我們前面的假設\(|B|>|A|\)是矛盾的,所以假設不成立,得證。
? 我們將\(M=(S,I)\)看成一個帶權擬陣,每個\(S\)中的元素的權值就是對應的堆中火柴的數量,那麽運用貪心算法我們就可以在帶權擬陣中找出權值最大的基
所以就能直接用貪心做啦
?
代碼大概長這個樣子:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
using namespace std;
const int MAXN=110,UP=30;
int a[MAXN];
int n,m;
ll ans;
namespace xxj{
int a[UP+1];
bool insert(int x){
for (int i=UP;i>=0;--i)
if (x&(1<<i)){
if (!a[i]){
a[i]=x;
break;
}
x^=a[i];
}
return x;
}
}
int main(){
#ifndef ONLINE_JUDGE
freopen("a.in","r",stdin);
#endif
scanf("%d",&n);
for (int i=1;i<=n;++i) scanf("%d",a+i);
sort(a+1,a+1+n);
ans=0;
for (int i=n;i>=1;--i)
if (!xxj::insert(a[i]))
ans+=a[i];
printf("%lld\n",ans);
}
【bzoj3105】新Nim遊戲