codeforces #334 div1 603C Lieges of Legendre(博弈)

来源:互联网 发布:ntp网络时间服务器 编辑:程序博客网 时间:2024/06/15 20:28

题目链接:

codeforces 603C

题目大意:

有两个人做游戏,游戏规则如下:
有n堆石子,每次可以对一堆石子进行操作,如果当前石子是偶数,那么可以选择将这2*x个石子分成k堆石子数为x的石子堆,还有一种没有前提的操作是取走当前堆的一个石子,问先手赢还是后手赢,先手和后手都足够聪明的情况下。

题目分析:

首先对于这种组合游戏的题目,很容易想到利用SG函数来解。我们对于游戏的局势进行分类讨论:

  • 当k是偶数的情况下,
    • 我们可以知道如果要把一个偶数堆分成k个堆,相当于将局势转到一个新的组合游戏,这个组合游戏的每个堆规模相同,都为x,那么
      f(2x)=f(x)f(x)f(x)k times=0
    • 对于奇数堆的情况,我们可以一步走到偶数堆,所以状态就是1了
  • 当k是奇数的情况,就相对麻烦一些了,
    • 我们考虑偶数堆的情况,同上得到
      f(2x)=f(x)f(x)f(x)k times=f(x)
      ,那么可以递归地求取结果,显然可以算出前几项的f(x)的值,那么可以递推知道f(x)的值是大于0的。
    • 考虑奇数堆的情况,我们知道可以一步走到偶数堆,而偶数堆是大于0的,所以奇数堆一定是0
    • 其中关于偶数堆大于0的情况证明起来就是通过前几项递推发现偶数项均大于0,而后面的奇数项状态只能转移到之前的偶数项,所以0状态不可达,那么它一定是0状态,而后面的偶数因为减1的转移状态已经可达0,那么状态一直传递下去,就能够归纳出这条性质。
  • 一些不符合这些性质的小数据,可以直接通过打表得出,不会超过5,而且状态额转移方法很少,手算起来也很简单。

AC代码:

#include <iostream>#include <cstring>#include <cstdio>#include <algorithm>#define MAX 100007using namespace std;int n,k;int a;int f[MAX];int dfs ( int x ){    if ( x < 6 ) return f[x];    if ( x&1 ) return 0;    else        return dfs(x/2)==1?2:1;}int main ( ){    while ( ~scanf ( "%d%d" , &n , &k ) )    {        int sum = 0;        for ( int i = 0 ; i < n ; i++ )        {            scanf ( "%d" , &a );            if ( k&1 )            {                        f[0] = f[2] = f[5] = 0;                f[1] = f[3] = 1;                f[4] = 2;                if ( a < 6 )                    sum^= f[a];                else                    sum^= a&1?0:dfs(a);            }            else            {                f[0] = f[3] = 0;                f[1] = f[4] = 1;                f[2] = 2;                if ( a < 5 )                    sum ^= f[a];                else                     sum ^=  a&1?0:1;            }        }        if ( sum ) puts ("Kevin");        else puts("Nicky");     }}
1 0