牛客 取石子游戲
題意:
數據範圍: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;
}