Description
传统的Nim游戏是这样的:有一些火柴堆,每堆都有若干根火柴(不同堆的火柴数量可以不同)。两个游戏者轮流操作,每次可以选一个火柴堆拿走若干根火柴。可以只拿一根,也可以拿走整堆火柴,但不能同时从超过一堆火柴中拿。拿走最后一根火柴的游戏者胜利。
本题的游戏稍微有些不同:在第一个回合中,第一个游戏者可以直接拿走若干个整堆的火柴。可以一堆都不拿,但不可以全部拿走。第二回合也一样,第二个游戏者也有这样一次机会。从第三个回合(又轮到第一个游戏者)开始,规则和Nim游戏一样。
如果你先拿,怎样才能保证获胜?如果可以获胜的话,还要让第一回合拿的火柴总数尽量小。
Input
第一行为整数k。即火柴堆数。第二行包含k个不超过109的正整数,即各堆的火柴个数。
Output
输出第一回合拿的火柴数目的最小值。如果不能保证取胜,输出-1。
Sample Input
6
5 5 6 6 5 5
Sample Output
21
HINT
k<=100
Source
我们要做的事情是很显然的
拿掉一些堆后使得剩下的火柴不存在一个子集异或为0
也就是我们要求一个极大线性无关组
可以证明这是一个拟阵
于是我们就可以从大到小添加元素动态维护线性基了
有关拟阵的证明:(转)
我们设
n
n
n个火柴堆的数目为集合
S
S
S,若某个
S
S
S的子集
r
r
r不存在任何一个非空子集异或和0,则
r
∈
I
r∈I
r∈I.下面我们证明二元组
M
=
(
S
,
I
)
M=(S,I)
M=(S,I)是一个拟阵。
遗传性:设
A
∈
I
A∈I
A∈I,则
A
A
A是
S
S
S的线性无关组,则
A
A
A的任意非空子集均线性无关,即对
A
A
A的任意子集
B
B
B,
B
B
B均线性无关,因此
B
∈
I
B∈I
B∈I,证毕。
交换性:设
A
,
B
∈
I
A,B∈I
A,B∈I,且
∣
A
∣
<
∣
B
∣
|A|<|B|
∣A∣<∣B∣,我们要证明存在
x
∈
B
x∈B
x∈B,使得
A
∪
{
x
}
∈
I
A∪\{x\}∈I
A∪{x}∈I.利用反证法,假设对于任意
x
∈
B
−
A
x∈B-A
x∈B−A,均有
A
∪
{
x
}
A∪\{x\}
A∪{x}不属于
I
I
I,则
B
−
A
B-A
B−A中的元素均在
A
A
A的异或空间中,可由
A
A
A的子集异或和表示。
因此B中的元素都在A的异或空间中。那么必然有B的异或空间包含于A的异或空间。由
∣
A
∣
<
∣
B
∣
|A|<|B|
∣A∣<∣B∣且
A
,
B
A,B
A,B线性无关,显然矛盾。因此交换性存在,证毕。
从而我们可以使用贪心算法确定最优解。
#include<bits/stdc++.h>
using namespace std;
#define rep(i,j,k) for(int i = j;i <= k;++i)
#define repp(i,j,k) for(int i = j;i >= k;--i)
#define rept(i,x) for(int i = linkk[x];i;i = e[i].n)
#define P pair<int,int>
#define Pil pair<int,ll>
#define Pli pair<ll,int>
#define Pll pair<ll,ll>
#define pb push_back
#define pc putchar
#define mp make_pair
#define file(k) memset(k,0,sizeof(k))
#define ll long long
int rd()
{
int sum = 0;char c = getchar();bool flag = true;
while(c < '0' || c > '9') {if(c == '-') flag = false;c = getchar();}
while(c >= '0' && c <= '9') sum = sum * 10 + c - 48,c = getchar();
if(flag) return sum;
else return -sum;
}
int a[101],b[35],n;
ll ans;
bool mycmp(int a,int b){return a>b;}
int main()
{
n = rd();rep(i,1,n) a[i] = rd(),ans+=a[i];
sort(a+1,a+n+1,mycmp);
rep(i,1,n)
{
int tmp = a[i];
repp(j,30,0)
if(a[i]>>j&1)
if(!b[j])
{
b[j] = i;
break;
}
else a[i] ^= a[b[j]];
if(a[i]) ans -= tmp;
}
printf("%lld\n",ans);
return 0;
}