牛客国庆集训派对Day3: G. Stones(博弈+SG)

版权声明:本文为博主原创文章,你们可以随便转载 https://blog.csdn.net/Jaihk662/article/details/82933063

 

G. Stones

题目描述

有n堆石子,第i堆石子有xi个。
修修和栋栋轮流取石子,每人每次需要从任意一堆石子中取走个,修修先手。无法操作的人失败。此外,如果一个人取完了一堆石子,他会立即获胜。
不巧的是,修修除了数数以外啥都不会,他希望你帮他求出他能否获胜。

输入描述:

第一行一个整数t表示数据组数 (1 ≤ t ≤ 1000)。
每组数据第一行三个整数n,a,b (1 ≤ n ≤ 1000,1≤ a ≤ b ≤ 109),第二行n个整数 (1 ≤ xi ≤ 109)。

输出描述:

每组数据输出一行一个字符串:如果修修可以获胜输出Yes,否则输出No

输入

2
1 1 3
4
1 1 3
6

输出

No
Yes

其实就是简单的Nim博弈,求出每一堆石子的SG(),然后异或起来就好了

不过这题多了一个条件:每个人都可以取[a, b]个,但若一个人取完了一堆石子,他会立即获胜

那么就相当于多了三种情况:

  1. 如果一开始某堆石子的范围就在[a, b]内,很显然先手直接获胜
  2. 如果某堆石子的范围是[0, a),那么这堆和没有一样
  3. 如果某堆石子个数大于b个,那么在求SG时,所有≤b的状态都不可达,因为到达就必输

然后就是常规操作了

#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<map>
#include<string>
#include<math.h>
#include<queue>
#include<stack>
#include<iostream>
using namespace std;
#define LL long long
#define mod 1000000007
int L, R;
int SG(int x)
{
	if(x<=R)
		return 0;
	if(L==1)
		return x%(L+R);
	x -= R;
	x %= (L+R);
	x /= L;
	if(x<=1)
		x ^= 1;
	return x;
}
int main(void)
{
	int T, n, i, x, ans, p;
	scanf("%d", &T);
	while(T--)
	{
		ans = p = 0;
		scanf("%d%d%d", &n, &L, &R);
		for(i=1;i<=n;i++)
		{
			scanf("%d", &x);
			if(x>=L && x<=R)
				p = 1;
			ans ^= SG(x);
		}
		if(p==1 || ans)
			printf("Yes\n");
		else
			printf("No\n");
	}
}
/*int cnt, a, b, tak[1005], Hash[1005], sg[1005];
void Getsg(int n)  
{  
	int i, j;  
	memset(sg, 0, sizeof(sg));
	for(i=1;i<=n;i++)
	{
		if(i>=a && i<=b)
			continue;
		memset(Hash, 0, sizeof(Hash));
		for(j=1;tak[j]<=i&&j<=cnt;j++)
		{
			if(i-tak[j]>=a && i-tak[j]<=b)
				continue;
			Hash[sg[i-tak[j]]] = 1;
		}
		for(j=0;j<=n;j++)
		{
			if(Hash[j]==0)
			{
				sg[i] = j;
				break;
			}
		}
	}
}
int main(void)
{
	int i;
	while(scanf("%d%d", &a, &b)!=EOF)
	{
		cnt = 0;
		memset(tak, 0, sizeof(tak));
		for(i=a;i<=b;i++)
			tak[++cnt] = i;
		Getsg(30);
		for(i=1;i<=30;i++)
			printf("%d ", sg[i]);
		puts("");
	}
	return 0;
}
*/

猜你喜欢

转载自blog.csdn.net/Jaihk662/article/details/82933063
今日推荐