版权声明: https://blog.csdn.net/weixin_39792252/article/details/81448795
G. Stones
题目:题意很简单啦,就是两个人分别从n堆石子中拿石子,每次只能拿(a~b)个,不能取的就失败,并且如果一个人取完了一堆就立即获胜;
两个条件:
1.从n堆石子轮流取(a~b)个石子,不能取者失败;
2.如果一个人取完一堆就立即获胜;
那么SG函数怎么打表,首先一个坑就是如果有某一堆石子的数量在a~b之间,则先手必胜;
那么就要特判,0 - a-1 的sg值是0,将a~b设置为不可达状态,然后求出其他点的sg状态值,观察就可以发现规律吧!
首先,如果存在a<=xi<=b,显然先手获胜。
否则,我们将a~b设为不可到达的状态,
然后求其它状态的sg值。
当a=1时,从b+1开始,sg值是0~a+b-1不断循环。
当a>1时,0~a-1的sg值为0,从b开始(假设b的sg值为1)sg值是1 02 3 ...这样的循环,循环节长度为a+b,每段长度为a。
时间复杂度O(tn).
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn = 1e5+7;
ll x[maxn], a, b, n;
int SG[maxn],S[maxn];
void getSG(int n){
memset(SG, 0, sizeof(SG));
for(int j = a; j <= b; j++) SG[j] = -1;
for(int i = 1; i <= n; i++){
memset(S,0,sizeof(S));
if(i>=a&&i<=b) continue;
for(int j = i-b; j <= i-a; j++)
if(j>=0&&SG[j] != -1)S[SG[j]] = 1;
while(S[SG[i]]) SG[i]++;
printf("%d %d\n", i, SG[i]);
}
}
ll check(ll m) {
m %= (a+b);
m /= a;
if(m == 0) return 1;
else if(m == 1) return 0;
else return m;
}
int main()
{
//printf("a , b : %d %d\n", 2, 5);
//getSG(100, 1, 6);
//cout<<endl;
int t; scanf("%d", &t);
while(t--)
{
scanf("%lld%lld%lld", &n, &a, &b);
for(int i = 0; i < n; i++) scanf("%lld", &x[i]);
bool f = false;
ll res = 0;
for(int i = 0; i < n; i++) if(x[i]>=a&&x[i]<=b) { f = true; break; }
if(f) { puts("Yes"); continue; }
if(a == 1) for(int i = 0; i < n; i++) res ^= ((x[i] - b - 1)%(a+b));
else for(int i = 0; i < n; i++)
if(x[i] > b) res ^= check(x[i] - b);
if(res == 0) puts("No");
else puts("Yes");
}
}