题意
给定 n n n堆石子,两个人可以各取走任意堆石子,但不能取完,可以不取。接下来和 N i m Nim Nim游戏规则一样,询问是否有先手必胜策略。
思路
关于一般
N
i
m
Nim
Nim博弈游戏有这样一个结论:
a
1
⊕
a
2
⊕
.
.
.
⊕
a
n
!
=
0
a_1\oplus a_2\oplus ... \oplus a_n!=0
a1⊕a2⊕...⊕an!=0,那么先手必胜,反之如果等于0,先手必败
我们从先手的角度看:
如果我是先手,我一定不会给后手把剩余集合异或值改成
0
0
0的机会(因为后手拿掉一些元素后可能把剩余集合的异或变成
0
0
0,形成先手必败的局面)
有了这样的想法以后,我们就可以按照这个思想开始考虑做法了。
做法:
因为要最小的可能值,所以我们先贪心按照从大到小排序,然后判断每一堆插入线性基后是否异或值为
0
0
0,是就加到
a
n
s
ans
ans里面,不是就插入到线性基中。
至于贪心的证明可参照证明。
附上代码,简单易懂!
代码
#include <cctype>
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
#define ph puts("")
typedef long long ll;
template <class T>
void rd(T &x)
{
x = 0;
int f = 1;
char c = getchar();
while (!isdigit(c)) {if (c == '-') f = -1; c = getchar();}
while (isdigit(c)) x = (x << 3) + (x << 1) + (c ^ 48), c = getchar();
x *= f;
}
template <class T>
void pt(T x)
{
if (x < 0)
putchar('-'), x = (~x) + 1;
if (x > 9)
pt(x / 10);
putchar(x % 10 ^ 48);
}
const int N = 105;
int n, a[N], p[35];
void add(int x)
{
for (int i = 30; ~i; i--)
if (x & (1 << i))
{
if (!p[i])
{
p[i] = x;
break;
}
x ^= p[i];
}
return;
}
int query(int x)
{
for (int i = 30; ~i; i--)
if (x & (1 << i))
x ^= p[i];
return x;
}
ll ans;
int main()
{
rd(n);
for (int i = 1; i <= n; i++)
rd(a[i]);
sort(a + 1, a + n + 1);
for (int i = n; i; i--)
if (!query(a[i]))
ans += a[i];
else
add(a[i]);
pt(ans);
return 0;
}