bzoj3105 [cqoi2013]新Nim游戏
Description
传统的Nim游戏是这样的:有一些火柴堆,每堆都有若干根火柴(不同堆的火柴数量可以不同)。两个游戏者轮流操作,每次可以选一个火柴堆拿走若干根火柴。可以只拿一根,也可以拿走整堆火柴,但不能同时从超过一堆火柴中拿。拿走最后一根火柴的游戏者胜利。
本题的游戏稍微有些不同:在第一个回合中,第一个游戏者可以直接拿走若干个整堆的火柴。可以一堆都不拿,但不可以全部拿走。第二回合也一样,第二个游戏者也有这样一次机会。从第三个回合(又轮到第一个游戏者)开始,规则和Nim游戏一样。
如果你先拿,怎样才能保证获胜?如果可以获胜的话,还要让第一回合拿的火柴总数尽量小。
Input
第一行为整数k。即火柴堆数。第二行包含k个不超过109的正整数,即各堆的火柴个数。
Output
输出第一回合拿的火柴数目的最小值。如果不能保证取胜,输出-1。
Sample Input
6
5 5 6 6 5 5
5 5 6 6 5 5
Sample Output
21
HINT
k<=100
正解:线性基+博弈论。
第一次自己想出线性基的题。。好感动啊qwq!!!话说这个数据范围这么小是什么鬼。。
我们先考虑普通的nim游戏。如果火柴堆的异或和不为0,那么先手胜,否则后手胜。那么现在有一个拿走任意堆石子的操作。我们可以考虑一下,先手拿了若干堆石子,使得后手无论如何取石子,都无法让所有石子异或和为0。那么这就很明显了,我们需要构造一个线性基,线性基以外的石子堆都要被拿走。然后我们要求拿走石子最小的方案,那这就和元素那道题的贪心很像了。我们先把石子从大到小排序,然后依次插入线性基,如果这个数最后变成了0,就把它加入答案。最后的总和就是我们要求的答案。证明。。拟阵我不会
1 //It is made by wfj_2048~ 2 #include <algorithm> 3 #include <iostream> 4 #include <complex> 5 #include <cstring> 6 #include <cstdlib> 7 #include <cstdio> 8 #include <vector> 9 #include <cmath> 10 #include <queue> 11 #include <stack> 12 #include <map> 13 #include <set> 14 #define inf (1<<30) 15 #define il inline 16 #define RG register 17 #define ll long long 18 #define File(s) freopen(s".in","r",stdin),freopen(s".out","w",stdout) 19 20 using namespace std; 21 22 ll a[110],p[70],n,ans; 23 24 il int gi(){ 25 RG int x=0,q=1; RG char ch=getchar(); while ((ch<'0' || ch>'9') && ch!='-') ch=getchar(); 26 if (ch=='-') q=-1,ch=getchar(); while (ch>='0' && ch<='9') x=x*10+ch-48,ch=getchar(); return q*x; 27 } 28 29 il int cmp(const ll &a,const ll &b){ return a>b; } 30 31 il int insert(RG ll x){ 32 for (RG int i=62;i>=0;--i) 33 if (x>>i&1){ 34 if (!p[i]){ p[i]=x; break; } 35 x^=p[i]; 36 } 37 return x; 38 } 39 40 il void work(){ 41 n=gi(); for (RG int i=1;i<=n;++i) a[i]=gi(); 42 sort(a+1,a+n+1,cmp); 43 for (RG int i=1;i<=n;++i) 44 if (!insert(a[i])) ans+=a[i]; 45 printf("%lld\n",ans); return; 46 } 47 48 int main(){ 49 File("nim"); 50 work(); 51 return 0; 52 }