Link:https://www.lydsy.com/JudgeOnline/problem.php?id=1228
Solution:
感觉自己对博弈论的理论一直了解得不够透彻
一篇讲原理的文章:Sprague-Grundy定理是怎么想出来的
现在发现其实可以将SG函数的合成看作为Nim游戏,也顺便能证明其异或运算的正确性了
对于此题,发现每两堆之间明显是独立的,于是只要求出每组的SG值再异或即可
但直接求解SG复杂度过高,于是采取打表找规律的方式
0出现条件:i,j均%2=1
1出现条件:i,j均%4=1或2
2出现条件:i,j均%8=1或2或3或4
得到sg(i,j)=k的必要条件:
(i-1)%2^(k+1) < 2^k && (j-1)%2^(k+1) < 2^k
这样便能在O(logS)时间内求出SG函数
Code:
#include <bits/stdc++.h> using namespace std;
typedef long long ll;
int t,n,x,y; int SG()
{
ll pro=;
for(int i=;;++i,pro<<=)
if(((x-)&(pro-))<(pro>>) && ((y-)&(pro-))<(pro>>)) return i;
} int main()
{
scanf("%d",&t);
while(t--)
{
scanf("%d",&n);int res=;
for(int i=;i<=n/;i++)
scanf("%d%d",&x,&y),res^=SG();
puts(res?"YES":"NO");
}
}
Review:
1、求解A%2^k的技巧:
直接求解 A&(2^k - 1) 即可,大大优化常数
2、求解SG函数的一般步骤:
(1) 使用 数组f 将 可改变当前状态的方式 记录下来。(一般SG函数成规律性变化)
(2) 然后我们使用 另一个数组 将当前状态x 的后继状态标记。
(3) 最后模拟mex运算,也就是我们在标记值中 搜索 未被标记值 的最小值,将其赋值给SG(x)。
3、如果求解SG函数复杂度过高,考虑打表找规律的方式