[算进] BZOJ3105 新NIM游戏(线性基)

Problem

ACwing 题目地址

BZOJ 题目地址

洛谷 题目地址

Solution

首先我们要知道:第一个人只要挑选出一些元素 (a_i) 后,使得剩下这个集合 (A) 的异或空间的张成不包含 (0),则先手必赢。(异或和不为 (0),NIM游戏先手必赢)

换言之,先手只要选对了,后手怎么选,先手都可以赢。那么先手怎么选才是对的呢?

选出一个极大的线性无关子集(异或空间下)。(这不恰恰是线性基能做的事吗?)

在满足上面这个条件下,要求选出来的元素和最小。因此我们直接贪心地将 (A) 集合从大到小排序,插入线性基。无法插入的元素即是我们要拎出来的元素,加入答案。留下来的集合就是原集合的极大的线性无关子集(异或空间下)

Code

Talk is cheap.Show me the code.

#include<bits/stdc++.h>
#define int long long
using namespace std;
inline int read() {
	int x=0,f=1; char ch=getchar();
	while(ch<'0' || ch>'9') { if(ch=='-') f=-1; ch=getchar(); }
	while(ch>='0'&&ch<='9') { x=(x<<3)+(x<<1)+(ch^48); ch=getchar(); }
	return x * f;
}
const int N = 107;
int n,ans;
int a[N];
struct LB {
	int d[N];
	int insert(int x) {
		int tmp = x;
		for(int i=31;i>=0;--i) {
			if((1<<i) & x) {
				if(d[i]) {
					x ^= d[i];
				} else {
					d[i] = x; return 0;
				}
			}
		}
		return tmp;
	}
}B;
bool cmp(int a,int b) {
	return a > b;
}
signed main()
{
	n = read();
	for(int i=1;i<=n;++i)
		a[i] = read();
	sort(a+1, a+1+n, cmp);
	for(int i=1;i<=n;++i) {
		int val = B.insert(a[i]);
		ans += val;
	}
	printf("%lld
",ans);
	return 0;
}
原文地址:https://www.cnblogs.com/BaseAI/p/12376256.html