题目
t(1<=t<=1e5)轮比赛,第一轮比赛主人公先手,
第i轮比赛给出si,ei(1<=si<=ei<=1e18),si为初始的值,ei为终止局面的值,
对于当前为a的值,操作的一方可以将其变成a+1或2a,
若一方操作之后,当前的数大于ei,则操作的这个人输,
第i轮输的人会在第i+1轮先手,t轮过后,最后一轮的胜负情况是整个局面的胜负情况
问,先手是否有策略最后一轮必胜,先手是否有策略最后一轮必败,输出1/0(能/不能)
思路来源
AlexPanda的B站讲解
自己之前做过的eoj原题https://blog.csdn.net/Code92007/article/details/100734299
题解
eoj原题是初始si=1,ei=n(n<=1e18),每次变成2*a或a+1,问是否先手必胜/必败
可以通过对n的值sg打表找规律,对本题不适用,因为本题有个si
本题中可以用logn的方法将ei的值递归小,其方法对那道题也适用
一、首先,考虑能否先手必胜,
(1)若e为奇,可以O(1)判出
s为奇,先手败,因为先手只能操作到小于e的偶态,从而后手操作到奇态
s为偶,先手胜,
(2)考虑若e为偶,此处用[v]表示v的值向下取整,
①若[e/2]<s<=e,则两边的操作者只能加1,若s也为偶,先手败;若s为奇,先手胜
②若[e/4]<s<=[e/2],由于(e/2,e]且为偶时先手败,所以这个范围的s可以直接选择*2转移,此时先手必胜
③若s<=[e/4],则(s,e)能否先手胜,等价于询问(s,e/4)能否先手胜,
考虑(s,e/4)局面获胜者为主人公,对手的操作使得当前值v已经落在(e/4,e/2]中,
此时主人公只要按(2)②步骤的策略,即可先手必胜,拿下(s,e)的局面
二、考虑能否先手必败,也就是先手操作了之后,后手面临的局面是必胜的,
(1)若[e/2]<s<=e,此时先手直接*2,即必败
(2)若s<=[e/2],则(s,e)能否先手败,等价于询问(s,e/2)能否先手胜,
考虑(s,e/2)的局面获胜者为主人公,对手的操作使得当前值v已经落在(e/2,e]中,
此时主人公按(1)的作死策略即可完成先手必败
三、考虑n轮的情形,
如果在第一轮,
先手既可以控制先手必胜,也可以控制先手必败,即可通过倒推实现后续比赛的胜负自由取值,此时break
否则如果既不能控制先手必胜,也不能控制先手必败,说明对手可以任意控制,也break
否则,如果只能控制一个,就拖入下一轮继续考虑,
每次考虑的都是先手能否胜/败,
如果在上一轮是winner的话,把本轮算出的答案取反,代表本轮是后手
代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=4e5+10;
typedef long long ll;
int t,win,lose,sec;//想赢 想输 后手
ll s,e;
bool dfs(ll x,ll y){
if(y&1){
if(x&1)return 0;
else return 1;
}
if(x>y/2){
if(x&1)return 1;
else return 0;
// return (x-y)%2;
}
if(x>y/4){
return 1;
}
return dfs(x,y/4);//x<=y/4
}
bool dfs2(ll x,ll y){
if(x>y/2){
return 1;
}
return dfs(x,y/2);
}
int main(){
scanf("%d",&t);
while(t--){
scanf("%lld%lld",&s,&e);
win=dfs(s,e);
lose=dfs2(s,e);
win^=sec;lose^=sec;//若后手 取反
sec=win;
if(win==lose)break;
}
printf("%d %d\n",win,lose);
return 0;
}