1. 程式人生 > >牛客國慶集訓派對Day3: G. Stones(博弈+SG)

牛客國慶集訓派對Day3: G. Stones(博弈+SG)

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;
}
*/