假设读者已了解 NIM游戏,简单来说先手必胜的条件是:
$x_1 \oplus x_2 \oplus x_3 \cdots \oplus x_k \ne 0$
考虑到另一个可以拿掉部分堆使得剩下的堆的异或和为0,所以我们要先把这些可能的堆都给拿掉。
假设现在一个堆不拿,通过高斯消元可以得到这些堆的异或基,这意味着其他的堆都是可以被这些异或基的堆表示出来的,我们就需要拿掉这些剩余的不是异或基的堆。换句话来说,就是讲这个矩阵阶梯化后全0的行拿走。这个可以等价为将向量组中除极大线性无关组之外的向量剔除。极大线性无关组的内容可能有很多可能,但是向量的数量一定是固定的=$ra(m)$也就是这个矩阵的秩。
因此我们在高斯消元时每次将值最大的堆作为一个异或基,去消其他堆的元,最后剩余全零的堆就是我们要取走的,可以证明这样得到的答案是最小的。
C++ 代码
// #pragma GCC optimize(2)
#include<bits/stdc++.h>
#define xx first
#define yy second
using namespace std;
using ll = long long;
using ld = long double;
using PII = pair<ll, ll>;
const ll N = 1e5+7;
const ll inf = 1e18;
const ld eps = 1e-8;
const ll mod = 1e9+7;
int a[N], b[N];
void solve()
{
int n;
cin >> n;
for (int i = 1; i <= n; i ++){
cin >> a[i];
b[i] = a[i];
}
ll res = 0;
for (int i = 1; i <= n; i ++){
for (int j = i + 1; j <= n; j ++)
if (b[j] > b[i]){
swap(b[j], b[i]);
swap(a[j], a[i]);
}
if (a[i] == 0){
res += b[i];
continue;
}
int k = log2(a[i]);
for (int j = 1; j <= n; j ++){
if (i == j) continue;
if (a[j] >> k & 1) a[j] ^= a[i];
}
}
cout << res << '\n';
}
signed main()
{
ios::sync_with_stdio(0), cin.tie(0);
cout.tie(0);
ll tt = 1;
// cin >> tt;
while(tt --) solve();
return 0;
}