栗酱的异或和(取石子问题)

题目描述:
  栗酱特别喜欢玩石子游戏,就是两个人玩,有n堆石子,每堆有ai个,每次一个人可以轮流选择任意一堆,取走任意多的石子(但不能不取),谁先不能取谁输。
  栗酱觉得这个游戏很有趣,知道有一天,小太阳告诉她,其实如果两个人足够聪明,游戏的结局一开始就已经注定。
  栗酱是一个冰雪聪明的女孩子,她不相信,希望你演示给她看。

输入描述:
  多组数据,数据第一行T表示数据组数。每组数据第一行一个n,k表示一共有n堆石子,接下来你试图从第k堆开始取,从第二行开始,每隔一个空格一个第i堆石子的数量ai。
n≤105, ai≤109

输出描述:

输出“Yes”或“No”代表从该堆开始取是否可以必胜(如果足够聪明)。

示例1
输入

2
3 2
1 2 3
2 1
2 1

输出

No
Yes

说明

小太阳哥哥说,如果想赢,就试图把每堆石子数量的异或和变为0,最终便可以获得胜利,不相信自己证一下。


  分析:首先这种取石子的问题,emmmm就是试图把每堆石子数量的异或和变为0。首先有一个概念。
  平衡状态,又称作奇异局势。当面对这个局势时则会失败。任意非平衡态经过一次操作可以变为平衡态。每个玩家都会努力使自己抓完石子之后的局势为平衡,将这个平衡局势留给对方。因此,玩家A能够在初始为非平衡的游戏中取胜,玩家B能够在初始为平衡的游戏中取胜。
   对于一个普通的局势,如何判断其是不是奇异局势?对于一个局势(s1,s2,…sk),对所有石子个数做位的异或运算,s1^s2^s3^…^sk,如果结果为0,那么局势(s1,s2,…sk)就是奇异局势(平衡),否则就不是(非平衡)。 从二进制位的角度上说,奇异局势时,每一个bit位上1的个数都是偶数。


  所以说只要当前的局势异或和是0,也就是平衡状态的时候,比如全是1,1,1,1,a一个,b一个,a一个,b一个,那么a没的拿了a输了,所以谁没的拿谁输的规则下,把平衡状态留给对方的人会赢。如果一开始异或和0,没有获胜的可能性。如果开局是非平衡状态的情况下,异或和不等于,主人公有机会去赢,但是要求是把非平衡变成平衡。那么下面就是判断第一次拿是否把非平衡转化成平衡了。
  那么要如何做呢?假设 a < b< c,我们只要将 c 变为 a(+)b,即可,因为有如下的运算结果: a(+)b(+)(a(+)b)=(a(+)a)(+)(b(+)b)=0(+)0=0。要将c 变为a(+)b,只要从 c中减去 c-(a(+)b)即可。
  就是把面对的非奇异局势变为奇异局势留给对方。也就是从某一堆取出若干石子之后,使得每一个bit位上1的个数都变为偶数,这样的取法一般不只有一种。可以将其中一堆的石子数变为其他堆石子数的位异或运算的值(如果这个值比原来的石子数小的话)。
  也就是把原来的异或和再异或这一堆的个数,也就是相当于异或了其他所有的和,这是如果选的这一堆的个数大于等于这个异或和的时候,那么我们就可以取走相应的数量让个数与异或和相等,否则就不行。
  


#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
using namespace std;
#define ll long long int
#define INF 0x3f3f3f3f
const int maxn = 1e5 + 10;
ll a[maxn];
int main()
{
    int T; int n; int k;
    scanf("%d", &T);
    while (T--)
    {
        scanf("%d", &n);
        scanf("%d", &k); ll tmp = 0;
        ll ans = 0;
        for (ll i = 1; i <= n; i++)
        {
            scanf("%lld", &a[i]);
            if (i != k)
                ans = ans^a[i];
        }
        bool flag = 0;
        if (a[k]>ans)
            flag = 1;
        if (flag)
            printf("Yes\n");
        else
            printf("No\n");

    }
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值