题目描述:
栗酱特别喜欢玩石子游戏,就是两个人玩,有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;
}