分析

对于 \(nim\) 游戏的话,可以去看P2197进行博弈论相关知识的学习。

在得到结论之后,我们就是要求,所有车辆石头的异或和,是否为零,是则后手赢,反之先手赢。

我们考虑每一位来单独处理,计算这些车石头数在每一位有多少个 \(1\),对于第 \(i\) 位而言,若以函数的角度理解,它的周期是 \(2^{i+1}\),每一周期里,有 \(2^i\) 个数字该位为 \(1\)

因此对于每一位,我们就可以先找出 \(x\) 当前所处的连续 \(1\) 或连续 \(0\) 的片段最远延伸到了多少,然后再找出多少个满周期,最后再加上剩余的数里有多少个该位为 \(1\)

具体的实现可以详见代码。

#include<bits/stdc++.h>
using namespace std;
#define int long long
int cnt[60];
int cf[60];
int mx=57;
int n,x,mm;
signed main()
{
	cf[0]=1;
	for(int i=1;i<=mx;i++)cf[i]=cf[i-1]*2;
	cin>>n;
	for(int i=1;i<=n;i++){
		cin>>x>>mm;
		for(int j=0;j<=mx;j++){
			int m=mm;//这个东西导致我错了很久 
			int sum=x/cf[j]+1;//找出当前连续段的末端位置
			//每一连续段大小都是2^j,所以直接除后加一,乘上2^j减1就是所求 
			if(x&cf[j]){//x是从1开始,后面取完周期就是从0开始 
				sum=sum*cf[j]-1;
				if(sum-x+1>m)cnt[j]+=m;//特判 
				else {
					cnt[j]+=sum-x+1;
					m-=sum-x+1;
					sum=m/(cf[j]*2);//周期数 
					m-=cf[j]*2*sum;
					cnt[j]+=sum*cf[j];//一半0,一半1 
					m-=cf[j];//先有2^j个0 
					if(m>0)cnt[j]+=m;
				}
			}
			else {
				sum=sum*cf[j]-1;
				if((sum-x+1)>m)cnt[j]+=0;
				else {
					m-=sum-x+1;
					sum=m/(cf[j]*2);
					m-=cf[j]*2*sum;
					cnt[j]+=sum*cf[j];//前面同理 
					if(m>0)cnt[j]+=min(m,cf[j]);//前面就是1了 
				}
			}
			cnt[j]%=2;//因为是异或,直接模2即可 
		}
	}
	for(int i=0;i<=mx;i++){
		if(cnt[i]){//异或和不为0,先手必胜 
			puts("tolik");
			return 0;
		}
	}
	puts("bolik");
	return 0;
}
posted on 2021-09-10 07:47  漠寒·  阅读(49)  评论(0编辑  收藏  举报