作为我的第一道线性基题目,我觉得可以写一写题解。
考虑第二回合结束之后的情况,必须满足异或和不为0(博弈论的一般条件)。
于是可以发现,先手是尽量满足,对于第一回合结束后的状态,无论如何选取(不取玩),余下的堆异或和为正。
为了满足先手的做功最少,第一回合结束后棋子数量必须最多。
于是问题转变为:对于一些棋子,选出其中的几堆使得在这几堆中,任意的选法(不能不选)得到的异或和为正。
在我推理的时候发现一个性质:x ^ y <= x + y(易证)(如果你说x, y, 是复数或者实数我也没有办法,请你关闭这个网页吧)
于是考虑从大到小插入元素到线性基当中,如果插入成功,说明这一个元素是被选出来的。
正确性的伪证:
为什么要讲一个元素插入到线性基当中?
因为插入之后可以最大化答案(参见上面的问题转变为)。但是考虑到插入之后可能导致之后的元素无法插入,所以运用上面可能让你关网页的那个性质,对于x, y, z(x > y > z),如果满足y ^ z = x,一定是x与y被插入。开始的时候我写的是从小到大,于是全部WA了。
Code:
#include <bits/stdc++.h>
#define xx first
#define yy second
#define ll long long
#define _for(i,a,b) for(int i=(a);i<=(b);++i)
using namespace std;
struct _in {
const _in &operator, (int &a) const {
a = 0;
char k = getchar ();
int f = 1;
while (k > '9' || k < '0') {
if (k == '-') {
f = -1;
}
k = getchar ();
}
while (k >= '0' && k <= '9') {
a = a * 10 + k - '0';
k = getchar ();
}
a *= f;
return *this;
}
}in;
inline int readint () {
int x;
in, x;
return x;
}
const int N = 105;
ll info[N], n;
ll d[N];
ll sum, ans;
inline void insert (int u) {
ll tmp = u;
for (int i = 31; i >= 1; --i) {
if (u & (1 << (i - 1))) {
if (d[i]) {
u ^= d[i];
}
else {
d[i] = u;
ans += tmp;
return ;
}
}
}
}
int main () {
scanf ("%d", &n);
for (int i = 1; i <= n; ++i) {
scanf ("%d", &info[i]);
}
sort (info + 1, info + n + 1);
for (int i = n; i >= 1; --i) {//注意从n到1
sum += info[i];
if (info[i] != info[i - 1]) {//可以证明,重复元素只会留下一个
insert (info[i]);
}
}
printf ("%lld\n", sum - ans);
return 0;
}