CQOI2013 新Nim游戏 和 BZOJ1299 巧克力棒
新Nim游戏
传统的Nim游戏是这样的:有一些火柴堆,每堆都有若干根火柴(不同堆的火柴数量可以不同)。两个游戏者轮流操作,每次可以选一个火柴堆拿走若干根火柴。可以只拿一根,也可以拿走整堆火柴,但不能同时从超过一堆火柴中拿。拿走最后一根火柴的游戏者胜利。
本题的游戏稍微有些不同:在第一个回合中,第一个游戏者可以直接拿走若干个整堆的火柴。可以一堆都不拿,但不可以全部拿走。第二回合也一样,第二个游戏者也有这样一次机会。从第三个回合(又轮到第一个游戏者)开始,规则和Nim游戏一样。
如果你先拿,怎样才能保证获胜?如果可以获胜的话,还要让第一回合拿的火柴总数尽量小。
对于全部的测试点,保证 \(1 \leq k \leq 100\),\(1 \leq a_i \leq 10^9\)。
分析
https://blog.csdn.net/wyfcyx_forever/article/details/39477673
我们第一次拿完后,要使得剩下的火柴中不存在异或和为0的子集,否则对方会将先手必败的状态留给我们。
因此我们需要寻求极大的线性无关组,答案即为总和减去极大线性无关组的权值和。
显然存在线性无关组,因此必然存在解。
那么如何求解极大线性无关组呢?
我们能够证明这是一个拟阵,因此只需要从大到小排序,依次贪心的添加到当前集合就可以了。
时间复杂度\(O(k \log v)\),我猜出题人想让我们打高斯消元
co int N=101;
int k,a[N],b[N];
ll ans;
int main(){
read(k);
for(int i=1;i<=k;++i) ans+=read(a[i]);
std::sort(a+1,a+k+1);
for(int i=k,x;i;--i){
x=a[i];
for(int j=30;j>=0;--j)if(a[i]>>j){
if(!b[j]) {b[j]=a[i];break;}
a[i]^=b[j];
}
if(a[i]) ans-=x;
}
printf("%lld\n",ans);
return 0;
}
巧克力棒
TBL和X用巧克力棒玩游戏。每次一人可以从盒子里取出若干条巧克力棒,或是将一根取出的巧克力棒吃掉正整数长度。TBL先手两人轮流,无法操作的人输。他们以最佳策略一共进行了10轮(每次一盒)。你能预测胜负吗?
100%的分数,N<=14,L<=1,000,000,000。
题解
https://www.cnblogs.com/cjyyb/p/9484195.html
Nim博弈的变形形式。显然,如果我们不考虑拿巧克力棒出来的话,这就是一个裸的Nim博弈。
但是现在可以加入巧克力棒。加入巧克力棒的意义是修改当前的异或和。
如果不能够改变当前先后手赢的状态的话,那么必定不能够拿出一个巧克力棒的集合满足异或和为0。
初始情况下是先手必败的情况,因为先后不改变当前的必胜/必败情况,所以先手必须要拿出一个异或和为0的集合,并且使得剩下的部分不能够存在异或和为0的子集。不难证明如果剩下部分存在一个异或和为0的子集的话,必定也可以把这个子集拿出来使得不存在子集使得异或和为0。
那么,唯一需要判定的只剩下是否存在一个子集使得异或和为0了。直接线性基即可。
时间复杂度O(Tnlog),所以n其实想出多大出多大,因为线性基内的元素个数最多不会超过log个,否则必定会出现线性相关,即存在一个子集满足异或和为0。也就是说,真正的时间复杂度其实是O(T min(n,log)log)的,当n较大的时候瓶颈在于读入。
然而这题n小得可怜,直接暴力dfs都是可以的。
int bas[30];
bool insert(int x){
for(int i=29;i>=0;--i)if(x>>i&1){
if(!bas[i]) {bas[i]=x; return 1;};
x^=bas[i];
}
return 0;
}
void real_main(){
memset(bas,0,sizeof bas);
bool flag=0;
for(int n=read<int>();n--;)if(!insert(read<int>())) flag=1;
puts(flag?"NO":"YES");
}
int main(){
for(int T=10;T--;) real_main();
return 0;
}