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;
}

posted on 2020-07-13 22:13  autoint  阅读(167)  评论(0编辑  收藏  举报

导航