[bzoj2066][Poi2004]Gra——阶梯博弈Nim 大佬们的博客 Some Links

题目大意:

有m 个格子排成一行,从左到右编号1 到m,其中n 个给定的格
子里有石子,且编号为m 的格子里没有石子。两个人轮流操作,每次操
作要求选择一个石子,石子会移动到它右边第一个不含石子的格子里。
将某个石子移动到编号为m 的格子的人胜利,问先手有多少种操作方案
能使先手必胜。

思路:

首先倒数第二个格子上肯定不可以有格子,有的话就赢了,所以这是必胜状态。所以两个人都不想把石子移动到倒数第二个格子上面。考虑不得不移动到倒数第二个格子的情况,就是所有的石子都连续分布到倒数第三个格子,这个状态显然是必败的。
我们转化一下模型,发现连续的一列石子可以看成是一个阶梯上的,将两排石子变成相邻的相当于从相邻阶梯移动到相同阶梯,即每个空格算是一个阶梯,然后一排相邻的石子是放在它们左边第一个空白的格子上面的,然后就用阶梯博弈的方法去做就好了。
代码太丑了

/*==============================
 * Author : ylsoi
 * Problem : bzoj2066
 * Algorithm : Staricase Nim
 * Time : 2018.6.7
 * ============================*/
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<climits>
using namespace std;
void File(){
    freopen("bzoj2066.in","r",stdin);
    freopen("bzoj2066.out","w",stdout);
}
template<typename T>bool chkmax(T &_,T __){return _<__ ? (_=__,1) : 0;}
template<typename T>bool chkmin(T &_,T __){return _>__ ? (_=__,1) : 0;}
#define REP(i,a,b) for(register int i=a;i<=b;++i)
#define DREP(i,a,b) for(register int i=a;i>=b;--i)
#define MREP(i,x) for(register int i=beg[x];i;i=E[i].last)
#define mem(a) memset(a,0,sizeof(a))
#define ll long long
#define inf INT_MAX
const int maxn=1e6+10;
int m,n,pos[maxn],th[maxn],num[maxn],len[maxn],cnt_num,cnt,sum,ans;
int main(){
    File();
    scanf("%d%d",&m,&n);
    REP(i,1,n)scanf("%d",&pos[i]);
    if(pos[n]==m-1){
        DREP(i,n-1,1)if(pos[i]==pos[i+1]-1)
            ++ans;
        else break;
        printf("%d\n",ans+1);
        return 0;
    }
    pos[n+1]=m+2;
    DREP(i,n,1){
        if(pos[i]==pos[i+1]-1)num[i]=num[i+1];
        else{
            num[i]=++cnt_num;
            th[cnt_num]=th[cnt_num-1]+(pos[i+1]-pos[i]-1);
        }
    }
    DREP(i,n,0){
        if(num[i]!=num[i+1]){
            len[num[i+1]]=cnt;
            cnt=0;
        }
        ++cnt;
    }
    cnt=0;
    REP(i,1,cnt_num)if(th[i]%2==0)
        sum^=len[i];
    REP(i,1,cnt_num){
        if(th[i]==3)continue;
        REP(j,1,len[i])
            if(th[i]%2==0 && (sum^len[i]^(len[i]-j))==0)
                    ++ans;
            else if(th[i]%2==1){
                if(th[i-1]==th[i]-1 && (sum^len[i-1]^(len[i-1]+j))==0)
                        ++ans;
                else if(th[i-1]!=th[i]-1 && (sum^j)==0)++ans;
            }
    }
    printf("%d\n",ans);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/ylsoi/article/details/80610014