Solution [CQOI2013]新Nim游戏
题目大意:给定(n)堆石子,两个人可以各取走任意堆石子,但不能取完,可以不取。接下来和(Nim)游戏规则一样,询问是否有先手必胜策略
博弈论,线性基,贪心
分析:
首先明确先手必胜,证明:
假设有(n)堆,先手取(n-1)堆,第二个人因为不能取完只能不取,只剩一堆石子显然(Nim)和大于(0),因此先手必胜
再来看如何求解,假如先手取几堆石子后,第二个人想要获胜,操作后剩下石子的(Nim)和就必须为(0)
于是问题转化如下
先手取若干堆石子,使得剩下石子数不存在一个子集使得石子数量异或和为(0),换句话说,剩下的石头数在异或意义下是线性无关的
求第一次取的石头数可以反过来,求剩下的石子数,要使第一次取的石头数尽量少,剩下的石头权值和就要尽量大。也就是说,我们要求权值和最大的线性无关子集
考虑贪心,将石子数从大到小排序,依次插入线性基,插入失败说明选了该堆石子就构成了线性相关子集,必须第一次取掉它
因为异或是不进位意义下的加法,所以第一次取该堆石子一定比取和它线性相关的那多堆石子要优秀,其实就是[BJWC2011]元素这题
5 min淦紫题,弦形袋鼠真有意思
#include <cstdio>
#include <cctype>
#include <algorithm>
using namespace std;
inline int read(){
int x = 0;char c = getchar();
while(!isdigit(c))c = getchar();
while(isdigit(c))x = x * 10 + c - '0',c = getchar();
return x;
}
int n,val[128],base[32];
long long ans;
inline bool insert(int x){
for(int i = 30;i >= 0;i--){
if(!(x >> i))continue;
if(!base[i]){
base[i] = x;
return true;
}
x ^= base[i];
}
return false;
}
int main(){
n = read();
for(int i = 1;i <= n;i++)val[i] = read();
sort(val + 1,val + 1 + n,[](int a,int b){return a > b;});
for(int i = 1;i <= n;i++)
if(!insert(val[i]))ans += val[i];
printf("%lld
",ans);
return 0;
}