1. 程式人生 > >BZOJ 3105:[cqoi2013]新Nim遊戲

BZOJ 3105:[cqoi2013]新Nim遊戲

ont href name std oid lan max str sin

BZOJ 3105:[cqoi2013]新Nim遊戲

題目鏈接:http://www.lydsy.com/JudgeOnline/problem.php?id=3105

題目大意:在傳統的Nim取石子遊戲中做了改變:兩人剛開始可以取走任意堆石子(不包括全部)後進行傳統遊戲,問先手能否必勝,若必勝求出剛開始最少取多少石子。

線性基

傳統Nim遊戲先手必勝的前提條件為$a_0 \lxor a_1 \lxor a_2 \lxor ... \lxor a_{n-1} \neq 0$.

故若欲使新Nim遊戲先手必勝,則需保證先手剛開始取完後剩下的元素線性無關.

而問最少取多少石子,只需將原數列降序排序,貪心構造極大線性無關組.

代碼如下:

 1 #include <cstdio>
 2 #include <algorithm>
 3 #define MAX_BASE 30
 4 using namespace std;
 5 typedef long long ll;
 6 ll n,a[105],b[105];
 7 bool cmp(ll a,ll b){
 8     return a>b;
 9 }
10 ll cal(){
11     ll ans=0;
12     for(int i=0;i<n;++i){
13         ll t=a[i];
14 bool f=0; 15 for(int j=MAX_BASE;j>=0;--j){ 16 if(a[i]>>j&1){ 17 if(b[j])a[i]^=b[j]; 18 else{ 19 b[j]=a[i]; 20 f=1; 21 for(int k=j-1;k>=0;--k)if(b[k]&&(b[j]>>k&1
))b[j]^=b[k]; 22 for(int k=j+1;k<=MAX_BASE;++k)if(b[k]>>j&1)b[k]^=b[j]; 23 break; 24 } 25 } 26 } 27 if(f)ans+=t; 28 } 29 return ans; 30 } 31 int main(void){ 32 ll sum=0,ans; 33 scanf("%lld",&n); 34 for(int i=0;i<n;++i){ 35 scanf("%lld",&a[i]); 36 sum+=a[i]; 37 } 38 sort(a,a+n,cmp); 39 ans=cal(); 40 for(int i=0;i<n;++i) 41 printf("%lld\n",b[i]); 42 printf("%lld\n",sum-ans); 43 }

BZOJ 3105:[cqoi2013]新Nim遊戲