题面
题解
首先我们知道nim游戏先手必败当且仅当所有石堆异或和为0,因此我们的目标就是要使对手拿石堆的时候,无论如何都不能使剩下的石堆异或和为0。
对于一个局面,如果我们可以选取一些可以凑出0的石堆留下(因为不能全部拿走,所以这里至少要拿一堆),那么显然就先手必败了。 因此作为先手,我们留给后手的状态必须是一个凑不出0的状态。 考虑如果一个局面可以凑出0,会具有什么样的特征。 对于一个局面,我们求出它的线性基,如果在线性基外还有别的01串,那么根据线性基的定义,线性基内的串一定可以凑出外面的那个串,然后我们再把用线性基凑出的串和原来的串xor一下就得到0了。 因此我们需要使得留下的局面中,所有串都在线性基内。 所以我们先求出给定串的线性基,然后拿走所有线性基外的串就肯定先手必胜了。 同时为了使得拿走的尽量少,也就是留下的尽量多,所以我们按串的大小,从小到大往线性基内塞串即可#includeusing namespace std;#define R register int#define AC 110#define LL long longint n; LL ans;int s[AC], f[AC];inline int read(){ int x = 0;char c = getchar(); while(c > '9' || c < '0') c = getchar(); while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar(); return x;}void pre(){ n = read(); for(R i = 1; i <= n; i ++) s[i] = read(); sort(s + 1, s + n + 1);}void work(){ for(R i = n; i; i --)//从高开始贪心 { int x = s[i]; bool done = false; for(R j = 30, maxn = 1 << 30; ~j; j --, maxn >>= 1) { if(!(x & maxn)) continue; if(!f[j]) {f[j] = x, done = true; break;} else x ^= f[j]; } if(!done) ans += s[i];//没被加入线性基就要拿走 } printf("%lld\n", ans);}int main(){// freopen("in.in", "r", stdin); pre(); work();// fclose(stdin); return 0;}