题意
两个参与者在各自的第一回合都能拿若干个整堆的火柴,可不拿但不能全部拿走,从第二回合开始规则和Nim游戏一样。求 先手是否能必胜,必胜时先手在第一回合拿的最少的火柴数。
火柴堆数
k≤100
每堆火柴个数
a[i]≤109
Time
Limits:1000ms
Memory
Limits:512000KB
题意
由SG定理可以知道,先手肯定获胜,因为他可以在第一回合拿到只剩一堆火柴。
现在我们就是要求先手在第一回合拿的最少的火柴数。由SG定理可以知道,我们要在第一回合去掉一些数,使剩下的数任取若干个数异或值都不会等于0(若能,则后手在他的第一回合取掉其他的数,使剩下的数异或值为0,这样后手必胜)。
这样我们就是求极大线性无关基。
具体做法:将每个数转成二进制来看。对于所有数从大到小来做,当前做到的数
a[i]
,类似高斯消元,不断找到它当前二进制下最高位的
1
,然后将
代码
#include <cstdio>
#include <algorithm>
using namespace std;
typedef long long LL;
const int N = 110,M = 35;
int n,s[N],st[M],c[M];
LL ans;
bool cmp(int a,int b) {
return a > b;
}
int main() {
scanf("%d",&n);
for (int i = 1;i <= n;i ++) scanf("%d",&s[i]);
c[0] = 1;
for (int i = 1;i <= 30;i ++) c[i] = c[i - 1] << 1;
sort(s + 1,s + 1 + n,cmp);
for (int i = 1;i <= n;i ++) {
int cur = s[i];
for (int j = 30;j >= 0;j --) if (c[j] & s[i]) {
if (!st[j]) {
st[j] = i;
break;
}
s[i] ^= s[st[j]];
}
if (!s[i]) ans += LL(cur);
}
printf("%lld",ans);
}