博弈题

牛客 取石子游戏

题意:

在这里插入图片描述
数据范围:n<=1e18

思路:

首先应该能看出当前石子为k个的时候,其实就是分成k/2和k-k/2两分

然后就是:
1的时候必败,
2的时候分成1和1,拿走1剩下1,必胜,
3的时候分成1和2,拿走2上下1,必胜,
4的时候分成2和2,剩下2,必败

用必胜态和必败态手推15个左右,就会发现:
必胜是一段连续的,必败是一段连续的。
1必败
2必胜
3必胜

接下来轮到4=2+2,5=2+3,6=3+3,这三个必败,因为两份都是必胜态,因此可以推出:
假设必胜连续m次,那么接下来必败连续m+(m-1)次,其中m次是自加(例如2+2,3+3),(m-1)次是相邻(例如2+3)
因为两份都是必胜才是必败

假设必败连续m次,那么接下来必胜连续m+(m-1)次,其中m次自加,(m+1)次是相邻
因为两份只要有一份是必败就是必胜,所以必败的相邻是(m+1)而不是(m-1),
例如3=1+2,1是必败,2是必胜,可以推出3是必胜

因为n<=1e18,而每一段连续的要不就是连续m+(m-1)次,要不就是连续m+(m-1)次,基本上是翻倍的,
因此计算60次左右就到1e18了,直接预处理出每一段的长度,
对于每组循环,二分出给定的n应该在哪一段,根据那一段是必胜还是必败就可以知道答案了。

code:

//https://ac.nowcoder.com/acm/contest/4743/D
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int maxm=2e5+5;
vector<int>a{0,1,2},b{0,0,1},c{0,1,3};//a存一段的长度,b存对于的必胜必败态,c存a的前缀和(用于二分)
void init(){
    int len=a.size();
    int sum=c[len-1];
    while(sum<=1e18){
        int t=a[len-1];
        int tt=b[len-1];
        int aa=0;
        if(tt){
            aa=t-1+t;
        }else{
            aa=t+t+1;
        }
        sum+=aa;
        a.push_back(aa);
        b.push_back(tt^1);
        c.push_back(sum);
        len++;
    }
}
signed main(){
    init();
    int len=c.size();
    int T;
    cin>>T;
    while(T--){
        int n;
        cin>>n;
        int l=0,r=len-1;
        int ans=0;
        while(l<=r){
            int mid=(l+r)/2;
            if(n>c[mid]){
                ans=mid;
                l=mid+1;
            }else{
                r=mid-1;
            }
        }
        ans++;
        if(b[ans]){
            cout<<"XiaoHuiHui"<<endl;
        }else{
            cout<<"XiaoQiao"<<endl;
        }
    }
    return 0;
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章