题目
作为我的第一道线性基题目,我觉得可以写一写题解。
考虑第二回合结束之后的情况,必须满足异或和不为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; }