【SG函式】Gym - 101908 - B. Marbles
阿新 • • 發佈:2018-12-25
題目連結 <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"); }