题目描述
栗酱特别喜欢玩石子游戏,就是两个人玩,有n堆石子,每堆有ai个,每次一个人可以轮流选择任意一堆,取走任意多的石子(但不能不取),谁先不能取谁输。
栗酱觉得这个游戏很有趣,知道有一天,小太阳告诉她,其实如果两个人足够聪明,游戏的结局一开始就已经注定。
栗酱是一个冰雪聪明的女孩子,她不相信,希望你演示给她看。
输入描述:
多组数据,数据第一行T表示数据组数。
每组数据第一行一个n,k表示一共有n堆石子,接下来你试图从第k堆开始取,从第二行开始,每隔一个空格一个第i堆石子的数量ai。
n≤10^5
ai≤10^9
输出描述:
输出“Yes”或“No”代表从该堆开始取是否可以必胜(如果足够聪明)
NIM游戏
nim游戏的原型是这样的,有n堆石子,每次可以任意从某一堆中取任意的石子数,最后不能取的判负。
这种问题的通解是异或和,把所有堆的石子数异或起来,等于0是必败,反之必胜。
所谓必胜是能找到一种拿石子的方法,使得后手面对的是必败的状态。
所谓必败是不管怎么拿,后手都是一种必胜的状态。
以上结论已被证明必成立,证明过程略。
本题是nim游戏的一个变种
很容易可以发现本题与原型nim游戏之间最大的区别在于,先手第一次只能从第k堆取,之后就可以从任意一堆取。
那么,我们考虑两种情况:
- 所有堆的石子异或和为0,那么不管先手怎么拿都是必败的;
- 所有堆的石子数异或和不为0,那么就要考虑先手从第k堆中能不能拿到一些石子,使得后手面对的是一个异或和为0的情况(也就是必败态)。否则先手就是必败的。
我们详细来说第二种情况,什么情况下能拿到一些石子使得剩下的石子异或和为0呢?显然暴力的话时间会比较长。
假设有n堆石子,石子个数分别是a1,a2…an,第k堆个数为ak
那么此时应该有,a1a2…^an!=0,设异或和为x,从第k堆拿走bk个石子,那么我们就需要令 (ak-bk)=ak^x,
这样就会出现x^x = 0。
那么如何实现ak-bk = akx呢,其实只需要判断一下ak>akx就可以了
代码如下:
#include<iostream>
#include<cmath>
using namespace std;
typedef long long LL;
int main() {
int T;
cin >> T;
while (T--) {
int n , k ;
cin>>n>>k;//n堆石子,从第k堆开始取
int ak = 0;
int res = 0 ;
int ans ;//必败
for(int i = 0 ;i<n;i++){
int temp ;
cin>>temp ;
res^=temp ;//所有石子数的异或和
if(i==k-1) ak = temp;
}
if(res==0) ans = 0; //如果开始时异或和为0 先手必败
else{
if((ak^res) < ak) ans = 1;
else ans = 0 ;
}
if(ans == 1) puts("Yes");
else puts("No") ;
}
return 0;
}
链接:https://ac.nowcoder.com/acm/problem/14619
来源:牛客网