Description:
任何一个大学生对菲波那契数列(Fibonacci numbers)应该都不会陌生,它是这样定义的:
F(1)=1;
F(2)=2;
F(n)=F(n-1)+F(n-2)(n>=3);
所以,1,2,3,5,8,13……就是菲波那契数列。
在HDOJ上有不少相关的题目,比如1005 Fibonacci again就是曾经的浙江省赛题。
今天,又一个关于Fibonacci的题目出现了,它是一个小游戏,定义如下:
1、 这是一个二人游戏;
2、 一共有3堆石子,数量分别是m, n, p个;
3、 两人轮流走;
4、 每走一步可以选择任意一堆石子,然后取走f个;
5、 f只能是菲波那契数列中的元素(即每次只能取1,2,3,5,8…等数量);
6、 最先取光所有石子的人为胜者;
假设双方都使用最优策略,请判断先手的人会赢还是后手的人会赢。
思路:
其实刚刚接触SG函数做这个题目挺好的。SG函数就是一个DAG上的函数,每一个点表示当前局面的一种状态,然后往后连的边表示这个状态的转移,在博弈里面这样挺形象的。
我们如何分析是否先手必胜呢?
- 考虑整个博弈的逆过程,如果先手的状态就直接和终止状态有连边,那么无疑先手必胜。
- 如果有一个状态没有一个后继和先手必胜的点有连边的话,那么这个点必定先手必败。
- 然后我们通过数学归纳可以发现,一个点先手必胜,则后面只要有一条连接先手必败点就可以了。(博弈主角超级高的智商可以主动选择这个点,之后那个人的选择由于是必败点,所有又只可以选择必胜的点,然后就可以这样一直到达终点。)
- 同理,没有一条边连向必胜点的点为必败点。
然后这就是SG函数的低级版。
SG函数:
定义:
为
的后继节点的
值补集中的最小值。
然后这样值满足上面的条件的,为0的点为必败,其余为必胜。
然后会有一条优美的定理:当多个游戏叠加的时候,游戏的SG函数和等于所有子游戏的SG函数值得异或和。
证明可以这样:
终点的异或和为0,当目前异或和为0时,每次改动一个点必定会有1的出现。
当前的异或和为1时,必定存在一种方法使得为0。方法:记最后的和的位数为1的最高位为k,然后找到一个这一位为1的SG函数,改变状态,就可全部把1消掉了。
然后这题就把SG函数求出来在SG定理搞一波就没了。
/*=========================
* Author : ylsoi
* Problem : hdu1848
* Algorithm : SG
* Time : 2018.6.1
* ======================*/
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<climits>
using namespace std;
void File(){
freopen("hdu1848.in","r",stdin);
freopen("hdu1848.out","w",stdout);
}
template<typename T>bool chkmax(T &_,T __){return _<__ ? (_=__,1) : 0;}
template<typename T>bool chkmin(T &_,T __){return _>__ ? (_=__,1) : 0;}
#define REP(i,a,b) for(register int i=a;i<=b;++i)
#define DREP(i,a,b) for(register int i=a;i>=b;--i)
#define MREP(i,x) for(register int i=beg[x];i;i=E[i].last)
#define mem(a) memset(a,0,sizeof(a))
#define ll long long
#define inf INT_MAXj
const int maxn=1000+10;
int n,m,p,f[maxn],SG[maxn];
bool in[maxn];
int main(){
File();
f[1]=1;
f[2]=2;
REP(i,3,20)f[i]=f[i-1]+f[i-2];
REP(i,1,1000){
mem(in);
REP(j,1,20){
if(f[j]>i)break;
in[SG[i-f[j]]]=1;
}
REP(j,0,1000)if(!in[j]){
SG[i]=j;
break;
}
}
while(1){
scanf("%d%d%d",&n,&m,&p);
if(!n && !m && !p)break;
if(SG[n]^SG[m]^SG[p])puts("Fibo");
else puts("Nacci");
}
return 0;
}