博弈題

牛客 取石子游戲

題意:

在這裏插入圖片描述
數據範圍: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;
}

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