51nod 1661 黑板上的游戏(博弈sg函数找规律)

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
sgsg
k1
a1a1k=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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值