[bzoj3567]江南乐

题目描述

小A是一个名副其实的狂热的回合制游戏玩家。在获得了许多回合制游戏的世界级奖项之后,小A有一天突然想起了他小时候在江南玩过的一个回合制游戏。 游戏的规则是这样的,首先给定一个数F,然后游戏系统会产生T组游戏。每一组游戏包含N堆石子,小A和他的对手轮流操作。每次操作时,操作者先选定一个不小于2的正整数M (M是操作者自行选定的,而且每次操作时可不一样),然后将任意一堆数量不小于F的石子分成M堆,并且满足这M堆石子中石子数最多的一堆至多比石子数最少的一堆多1(即分的尽量平均,事实上按照这样的分石子万法,选定M和一堆石子后,它分出来的状态是固定的)。当一个玩家不能操作的时候,也就是当每一堆石子的数量都严格小于F时,他就输掉。(补充:先手从N堆石子中选择一堆数量不小于F的石子分成M堆后,此时共有N+M-1)堆石子,接下来小A从这N+M-1堆石子中选择一堆数量不小于F的石子,依此类推。
小A从小就是个有风度的男生,他邀请他的对手作为先手。小A现在想要知道,面对给定的一组游戏,而且他的对手也和他一样聪明绝顶的话,究竟谁能够获得胜利?

SG转移

显然我们可以分析出sg函数的转移,然后如果是分成m份,那么每一份都是 nm nm+1
这是什么!我们熟悉的下取整,其不同取值不超过根号个,于是可以分块啦。
nm+1 的个数是 n%m 也就是 nnmm 个,然后分析一下奇偶性即可。
建议使用记忆化搜索。

#include<cstdio>
#include<algorithm>
#define fo(i,a,b) for(i=a;i<=b;i++)
using namespace std;
const int maxd=300000+10;
int i,j,k,l,t,n,m,ca;
int sg[maxd];
bool bz[maxd];
void solve(int n){
    if (n<m){
        sg[n]=0;
        return;
    }
    if (sg[n]!=-1) return;
    int i=2,j,k,l,t;
    while (i<=n){
        j=n/(n/i);
        solve(n/i);
        if (n%i) solve(n/i+1);
        i=j+1;
    }
    i=2;
    while (i<=n){
        j=n/(n/i);
        k=sg[n/i];l=sg[n/i+1];
        if (i==j){
            t=0;
            if (((n/i+1)*i-n)%2) t^=k;
            if ((n-n/i*i)%2) t^=l;
            bz[t]=1;
        }
        else{
            if (n%2==0) bz[0]=1;else bz[k^l]=1;
            if ((n/i)%2==1&&n%2==1) bz[k]=1;
            if ((n/i)%2==1&&n%2==0) bz[l]=1;
            if ((n/i)%2==0&&n%2==1) bz[l]=1;
            if ((n/i)%2==0&&n%2==0) bz[k]=1;
        }
        i=j+1;
    }
    fo(i,0,maxd-10)
        if (!bz[i]) break;
    sg[n]=i;
    i=2;
    while (i<=n){
        j=n/(n/i);
        k=sg[n/i];l=sg[n/i+1];
        bz[0]=bz[k]=bz[l]=bz[k^l]=0;
        i=j+1;
    }
}                
int main(){
    freopen("game.in","r",stdin);freopen("game.out","w",stdout);
    scanf("%d%d",&ca,&m);
    fo(i,m,maxd-10) sg[i]=-1;
    while (ca--){ 
        scanf("%d",&n);
        l=0;
        fo(i,1,n){
            scanf("%d",&t);
            solve(t);
            l^=sg[t];
        }
        if (l) printf("1");else printf("0");
        if (ca) printf(" ");
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值