sdut——4541:小志志和小峰峰的日常(取石子博弈模板题 4合1)

Posted philo-zhou

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了sdut——4541:小志志和小峰峰的日常(取石子博弈模板题 4合1)相关的知识,希望对你有一定的参考价值。

小志志和小峰峰的日常

Time Limit: 1000 ms Memory Limit: 65536 KiB

Problem Description

小志志和小峰峰特别喜欢一起讨论一些很好玩的问题。 
小志志发现一个有趣的游戏,马上和小峰峰进行了分享。 
有 n 堆石子堆,每堆石子个数为 a[i]。 
到小志志的回合:小志志可以选取其中的一堆,拿至少 1 个最多 x 个石子。 
到小峰峰的回合:小峰峰可以选取其中的一堆,拿至少 1 个最多 y 个石子。 
小志志先手,回合交替进行,到该玩家回合如果无法操作,该玩家输。 
 

Input

输入一个 T,总共有 T 组测试数据。 
每组测试数据: 
第一行输入 n 代表有 n 堆石子堆。 
第二行输入 n 堆石子堆分别的数量。 
第三行输入两个用空格隔开的 x, y 分别代表小志志和小峰峰对于一堆石子最多能拿的数量。 
(1 <= n <= 1e5, 1 <= a[i], x, y <= 1e9)

Output

小志志赢输出 “xzz”,小峰峰赢输出 “xff”,答案不包含 “”。

Sample Input

3 
1 
3 
2 2 
2 
4 7 
4 5 
3 
3 4 7 
8 8

Sample Output

xff 
xzz 
xff


题解:

我们需要分类讨论:
x == y 的时候:经典的 sg
x != y 的时候:
任意的 a[i] <= min(x, y):Nim 博弈
否则至少存在一堆 a[i] > min(x, y):
如果 x > y:
我们把先手能拿的石子个数看成 y 的话,就是经典的 sg,如果亦或和不是 0 那么先手必
胜。
如果亦或和是 0,那么先手可以选择石子个数大于 y 的堆,拿掉 y+1 的石子个数。这样对
于对方而言亦或和还是 0。所以先手必胜。
所以无论如何:先手必胜
如果 x < y:
如果石子堆个数 > min(x, y) 的堆数大于 2:后手必胜,先手无论如何处理都会变成上述的
情况。
如果石子堆个数 > min(x, y) 的堆数 == 1:
先手肯定会选择 > min(x, y) 的石子堆进行操作,不然先手必败。
设这堆石子个数为 t,除去这堆石子的亦或和为 ans,你只有将这堆石子个数变成 ans &&
ans <= x 才能赢。
所以 t-x <= ans && ans <= x 先手必胜。
否则先手必败。

 

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N = 1e5+5;
string s[2];
ll n, x, y, T;
ll arr[N];

bool f1(ll ans)
    for(int i = 1; i <= n; i++)
    ans ^= arr[i]%(x+1);
    return ans != 0;


int main()

    s[1] = "xzz", s[0] = "xff";
    cin >> T;
    while(T--)
    
        cin >> n;
        for(int i = 1; i <= n; i++) cin >> arr[i];
        cin >> x >> y;
        if(x == y) cout << s[f1(0)] << endl; //经典sg
        else
            int flag = 0, ans = 0;
            for(int i = 1; i <= n; i++)
                ans ^= arr[i];
                if(arr[i] > min(x, y))
                    flag++;
            
            if(!flag)  // Nim博弈
                if(ans) cout << s[1] << endl;
                else cout << s[0] << endl;
            
            else
                if(x > y) cout << s[1] << endl;
                else
                    if(flag > 1) cout << s[0] << endl;
                    else
                        int t;
                        for(int i = 1; i <= n; i++)
                            if(arr[i] > min(x, y))
                                t = arr[i];
                                ans ^= arr[i];
                                break;
                            
                        
                        if(t - ans <= x && ans <= x)
                            cout << s[1] << endl;
                        else cout << s[0] << endl;
                    
                
            
        
    
    return 0;

 



以上是关于sdut——4541:小志志和小峰峰的日常(取石子博弈模板题 4合1)的主要内容,如果未能解决你的问题,请参考以下文章

Java练习 SDUT-2737_小鑫の日常系列故事——奇遇记

Java练习 SDUT-2733_小鑫の日常系列故事——石头剪子布

nyoj 135 取石子 NIM

取石子游戏

17.12.22 取石子问题

NYOJ-取石子