sg函数的应用
http://acm.hdu.edu.cn/showproblem.php?pid=4559
sg函数中最重要的是要弄清楚。。。状态分割、状态转移之间的关系
分割代表从头结点进入某一后继进行状态转移,下次还可以重复这个操作。对原点进行分割,sg = sg(1)^sg(2)....
转移代表从一点开始向其后继移动,但下次就只能从该后继开始。 sg(i) = min(k 不属于 {x | x = sg(j), j是i的后继 } ) sg(i) >= 0
通常来说分割后的子树是互不影响的,所以在确定使用状态分割还是转移的时候这个很关键
本题中,将矩形按竖列分割为:连续的空矩形,非空矩形。。。这样分是因为在这两者之内进行状态转移是互不相关的
sg[i]代表连续i个空矩形的sg值,其后继状态就是从左到右填充1个或连续4个矩形
#include <stdio.h>
#include <string.h>
const int MAX = 5000;
int sg[MAX];
bool flg[MAX], mmp[2][MAX];
int mex(int k)
{
if (sg[k] != -1 ) return sg[k];
memset(flg, 0, sizeof flg);
int i, j;
for ( i = 0; k-1-i>=i; i++)
{
int tmp = mex(i)^mex(k-i-1)^1;
flg[tmp] = 1;
}
for (i = 0; k-2-i>=i; i++)
{
int tmp = mex(i)^mex(k-2-i);
flg[tmp] = 1;
}
for (i = 0;;i++)
{
if (!flg[i])
{
sg[k] = i;
break;
}
}
return sg[k];
}
int main()
{
#ifndef ONLINE_JUDGE
freopen("in.txt", "r", stdin);
#endif
int i, j, k;
memset(sg, -1, sizeof sg);
for (i = 1, sg[0] = 0; i< 4750; i++) // 初始化
{
sg[i] = mex(i);
}
int t, n, m, cs = 0, x, y;
int res = 0;
scanf("%d", &t);
while (t--)
{
res = 0;
printf("Case %d: ", ++cs);
scanf("%d%d", &n, &m);
memset(mmp, 0, sizeof mmp);
for (i = 0; i< m; i++)
{
scanf("%d%d", &x, &y);
mmp[x-1][y-1] = 1;
}
mmp[0][n] = mmp[1][n] = 1;
for (k= 0, i = 0; i<= n; i++)
{
j = 0;
if (mmp[0][i]) j++;
if (mmp[1][i]) j++;
if (j)
res ^= sg[k]^(j&0x1), k = 0;
else
k++;
}
if (res) puts("Alice");
else puts("Bob");
}
return 0;
}