1661 黑板上的游戏
基准时间限制:1 秒 空间限制:131072 KB 分值: 40 难度:4级算法题 收藏 关注
Alice和Bob在黑板上玩一个游戏,黑板上写了n个正整数a1, a2, …, an,游戏的规则是这样的:
1. Alice占有先手主动权。
2. 每个人可以选取一个大于1的数字擦去,并写上一个更小的数字,数字必须是整数,然后由对方进行下一次操作。
3. 如果擦去的数字是 x (x > 1) ,则写上的数字不能比 x/k 小,但是要比 x 小。这里的除法为有理数除法。
4. 不可以擦去任何一个数字 1 ,如果当前无法找到一个数字进行操作,则当前方输。
假设Alice和Bob都会选择最优的策略,请问Alice是否存在必胜的方案?
Input
第一行两个空格隔开的正整数n和k,其中n表示数字的个数,k表示游戏的参数。
第二行n个空格隔开的正整数,其中第i个表示ai。
1 ≤ n ≤ 10^5, 2 ≤ k ≤ 10^18, 1 ≤ ai ≤ 10^18。
Output
如果存在必胜方案,则输出“Alice i y”,其中i和y表示先手的一种能必胜的操作:将第i个数修改为y。
如果没有,则输出“Bob”表示后手必胜。
(输出不含引号)
Input示例
4 2
2 3 3 3
Output示例
Alice 2 2
这题一开始感觉无从下手
其实仔细观察之后,可以直接手写sg值找规律
然后发现,sg值是递归的,可以直接求解
这就可以判断胜负了,但是这题还要求输出必胜策略
就枚举每一堆,然后这堆的sg[i],要变为sg[i]异或ans
就是已知sg值,去求最小的这个sg值的状态,然后如果不在范围里
就递归乘k加1上去
方程式a−1−a−1k=sg
不会直接看出来解,只好二分求解,因为有些位置是递归的
但是他们和下一个位置,求出来的sg一样,所以只要二分最小解就行
代码
#include <map>
#include <set>
#include <stack>
#include <queue>
#include <cmath>
#include <string>
#include <vector>
#include <cstdio>
#include <cctype>
#include <cstring>
#include <sstream>
#include <cstdlib>
#include <iostream>
#include <algorithm>
#pragma comment(linker,"/STACK:102400000,102400000")
using namespace std;
#define MAX 100005
#define MAXN 1000005
#define maxnode 205
#define sigma_size 2
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define lrt rt<<1
#define rrt rt<<1|1
#define middle int m=(r+l)>>1
#define LL long long
#define ull unsigned long long
#define mem(x,v) memset(x,v,sizeof(x))
#define lowbit(x) (x&-x)
#define pii pair<int,int>
#define bits(a) __builtin_popcount(a)
#define mk make_pair
#define limit 10000
//const int prime = 999983;
const int INF = 0x3f3f3f3f;
const LL INFF = 0x3f3f;
//const double pi = acos(-1.0);
const double inf = 1e18;
//const double eps = 1e-9;
const LL mod = 1e9+7;
const ull mx = 133333331;
/*****************************************************/
inline void RI(int &x) {
char c;
while((c=getchar())<'0' || c>'9');
x=c-'0';
while((c=getchar())>='0' && c<='9') x=(x<<3)+(x<<1)+c-'0';
}
/*****************************************************/
LL x[MAX];
LL b[MAX];
LL k;
LL sg(LL a){
if(a==1) return 0;
if((a-1)%k==0) return sg((a-1)/k);
else{
int tmp=0;
if(a%k!=0) tmp++;
return a-a/k-tmp;
}
}
LL find(LL a,LL y,LL c){
if(a>=y&&a<c) return a;
if((c-1)/k<a) return c;
return find(a*k+1,y,c);
}
LL solve(LL a){
LL l=1,r=1e18;
while(l<=r){
LL mid=(l+r)/2;
LL tmp=mid-1-(mid-1)/k;
if(tmp>=a) r=mid-1;
else l=mid+1;
}
return l;
}
int main(){
//freopen("in.txt","r",stdin);
int n;
cin>>n>>k;
LL ans=0;
for(int i=1;i<=n;i++){
scanf("%I64d",&x[i]);
b[i]=sg(x[i]);
ans^=b[i];
}
if(ans){
printf("Alice");
for(int i=1;i<=n;i++){
if(x[i]==1) continue;
LL tmp=ans^b[i];
tmp=solve(tmp);
tmp=find(tmp,x[i]/k+((x[i]%k==0)?0:1),x[i]);
if(tmp<x[i]){
printf(" %d %I64d\n",i,tmp);
break;
}
}
}
else printf("Bob\n");
return 0;
}