1. 程式人生 > >【SG函式】Gym - 101908 - B. Marbles

【SG函式】Gym - 101908 - B. Marbles


題目連結 <http://codeforces.com/gym/101908/problem/B>


題意:

有一個100*100的棋盤,棋盤上有若干個棋子。兩個人進行博弈,每次可以選擇一個棋子進行移動,每次可以向左,或者向上,或者左上三個方向移動任意的距離。如果把任意一個棋子移到(0,0)點則獲勝。給出n個棋子的座標,問先手能不能獲勝。


題解:

這題跟普通的Nim不一樣。Nim是所有點都移動至(0,0),移動停止才會獲勝。而這題是任意一點移動到(0,0)就獲勝。

首先可以特判一下,如果一個點在(0,y)(x,0)(x,x)那麼就肯定獲勝,這些都是必勝態,我們可以把這三條線排除在外考慮。

那麼(1,2)和(2,1)這兩個點就可以當作Nim的終點,所有點都移動至這兩個點移動就停止。這兩個點Sg函式就是0,這樣就形成了標準的有向Nim。更新Sg函式的時候跳過特判掉的三條線即可。


#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e2+7;
const int inf=1e4+7;
int sg[N][N];
bool s[N*N];
void getsg(){
    for(int i=1;i<=100;i++){
        for(int j=1;j<=100;j++){
            if(j==i) continue;
            memset(s,false,sizeof(s));
            for(int k=1;k<=100;k++){
                if(i-k>0&&i-k!=j) s[sg[i-k][j]]=true;
                if(j-k>0&&i!=j-k) s[sg[i][j-k]]=true;
                if(i-k>0&&j-k>0) s[sg[i-k][j-k]]=true;
            }
            for(int k=0;;k++){
                if(!s[k]){
                    sg[i][j]=k;
                    break;
                }
            }
        }
    }
}
int main()
{
    getsg();
    int n;
    scanf("%d",&n);
    int x,y,ans=0,flag=0;
    for(int i=1;i<=n;i++){
        scanf("%d%d",&x,&y);
        if(x==y) flag=true;
        ans^=sg[x][y];
    }
    if(flag||ans) printf("Y\n");
    else printf("N\n");
}