题意:有n个堆,每个堆的高度为pi,两人玩游戏,每次可以任选一堆,并选择小于此堆的高度h(h<=pi)把这堆分离成高度为h的堆和高度为pi%h的堆。堆高为1时,是不可再分的。问:先手赢还是后手赢。

题解:

SG函数,我们先预处理前2000的SG函数。这里每次求SG值时,后继状态(一个堆分为多个堆)我们是异或的,不像之前的(一个堆就分为一个堆)就 直接标记下后继状态。为什么呢?

之前状态转移是确定的,一堆转移后还是一堆(只是数量变了)可以直接确定转移后的SG函数值,现在是一堆转移成多堆,原来堆的sg函数就是分成各个自堆函数的异或和,这个可以根据SG定理可得:游戏和的SG函数等于各个游戏SG函数的Nim和,所以说把一个堆分成多个堆就是一个NIM游戏。后继状态就是各个子堆的异或和。

#include<iostream>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<cstdio>
#include<vector>
#include<map>
#include<set>
#include<queue>
#include<stack>
//#define _for(i,a,b) for(int i=a;i<=b;i++)
using namespace std;
typedef long long ll;
double eps=0.05;
ll mod=1e9+7;
const int INF=0x3f3f3f3f,inf =0x3f3f3f3f;
const int MAXN=2e3+10;
const int maxn = 1e7+10;
//ll inf=100000000000000;
//template<typename T>inline void read(T &x)
//{
//    x=0;
//    static int p;p=1;
//    static char c;c=getchar();
//    while(!isdigit(c)){if(c=='-')p=-1;c=getchar();}
//    while(isdigit(c)) {x=(x<<1)+(x<<3)+(c-48);c=getchar();}
//   x*=p;
//}
typedef unsigned long long ull;
const int N=1e5+7;
const double PI=acos(-1.0);
int SG[2010];
set<int> S;
void init()
{
    SG[1]=0;
    int tmp=0;
    for(int i=2;i<=2002;i++)
    {
       S.clear();
       for(int j=1;j<i;j++)
       {
           tmp=0;
           if((i/j)&1) tmp^=SG[j];//偶数异或和为0,奇数为本身
           if(i%j)
           tmp^=SG[i%j];
           S.insert(tmp);

       }
       for(int j=0;;j++)
       {
           if(!S.count(j)){
            SG[i]=j;break;
           }
       }
    }

}
int main()
{
    init();
    int n;
    scanf("%d",&n);
    int item,sum=0;
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&item);
        sum^=SG[item];
    }
    if(sum) puts("First");
    else puts("Second");
    return 0;
}
View Code