2018 ccpc camp day 3 G. Stones [ SG 简单博弈]

版权声明: 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");
     }
}

猜你喜欢

转载自blog.csdn.net/weixin_39792252/article/details/81448795