栗酱的异或和

题目描述
栗酱特别喜欢玩石子游戏,就是两个人玩,有n堆石子,每堆有ai个,每次一个人可以轮流选择任意一堆,取走任意多的石子(但不能不取),谁先不能取谁输。
栗酱觉得这个游戏很有趣,知道有一天,小太阳告诉她,其实如果两个人足够聪明,游戏的结局一开始就已经注定。
栗酱是一个冰雪聪明的女孩子,她不相信,希望你演示给她看。
输入描述:
多组数据,数据第一行T表示数据组数。
每组数据第一行一个n,k表示一共有n堆石子,接下来你试图从第k堆开始取,从第二行开始,每隔一个空格一个第i堆石子的数量ai。
n≤10^5
ai≤10^9
输出描述:
输出“Yes”或“No”代表从该堆开始取是否可以必胜(如果足够聪明)

NIM游戏

nim游戏的原型是这样的,有n堆石子,每次可以任意从某一堆中取任意的石子数,最后不能取的判负。

这种问题的通解是异或和,把所有堆的石子数异或起来,等于0是必败,反之必胜。

所谓必胜是能找到一种拿石子的方法,使得后手面对的是必败的状态。
所谓必败是不管怎么拿,后手都是一种必胜的状态。
以上结论已被证明必成立,证明过程略。

本题是nim游戏的一个变种

很容易可以发现本题与原型nim游戏之间最大的区别在于,先手第一次只能从第k堆取,之后就可以从任意一堆取。
那么,我们考虑两种情况:

  1. 所有堆的石子异或和为0,那么不管先手怎么拿都是必败的;
  2. 所有堆的石子数异或和不为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
来源:牛客网

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

_尤一

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值