Fibonacci NIM

280 篇文章 1 订阅
27 篇文章 0 订阅

题目

传送门 to VJ

题意概要
有一堆石子,最开始有 n n n 颗。按照如下操作进行。

  • 第一次操作,可以拿走任意颗,但不能拿完,也不能不拿。
  • 以后的操作,拿走石子的数量不超过上一次的两倍。不能不拿。
  • 拿走最后一颗石子的人将会获得胜利。

询问是否先手必胜。

数据范围与约定
1 < n < 2 31 1<n<2^{31} 1<n<231 ,以 n = 0 n=0 n=0 作为多组数据结束的标识。

思路

有关斐波那契

斐波那契数列是大家所熟知的了, a 0 = a 1 = 1 ∀ n ∈ [ 0 , + ∞ ) , a n + 2 = a n + a n + 1 a_0=a_1=1\\\quad\\\forall n\in[0,+\infty),a_{n+2}=a_{n}+a_{n+1} a0=a1=1n[0,+),an+2=an+an+1

它有如下几个性质,想必是大家烂熟于心的了:

  • ∀ n ∈ [ 0 , + ∞ ) , 2 a n ≥ a n + 1 \forall n\in[0,+\infty),2a_n\ge a_{n+1} n[0,+),2anan+1 且等号仅在 n = 1 n=1 n=1 处成立。
  • ∀ n ∈ [ 0 , + ∞ ) , 2 a n ≤ a n + 2 \forall n\in[0,+\infty),2a_n\le a_{n+2} n[0,+),2anan+2 且等号仅在 n = 0 n=0 n=0 处成立。

为了叙述方便,把 ∃ n ∈ [ 0 , + ∞ ) , a n = x \exist n\in[0,+\infty),a_n=x n[0,+),an=x 简称为“ x x x 是斐波那契数”。

齐肯多夫定理

试图用斐波那契数的和去表示一个正整数,结果如何?

重复如下步骤,直到 x = 0 x=0 x=0 为止。

  • 找到一个最大的 t t t ,使得 a t ≤ x ∧ a t + 1 > x a_t\le x\wedge a_{t+1}>x atxat+1>x
  • a t a_t at 作为 x x x 的划分中的一部分。为了消除影响,将 x x x 减去 a t a_t at

毫无疑问,这种划分将在有限步内结束,并且和一定恰好为最初给定的 x x x

而且,这个划分 不存在两个相同的斐波那契数,也不会出现两个相邻的斐波那契数

只需要注意到,第一步中满足 x < a t + 1 ⇔ x − a t < a t − 1 x<a_{t+1}\Leftrightarrow x-a_t<a_{t-1} x<at+1xat<at1 即可。

必胜情况

假设 石子数 x x x 不是斐波那契数,那么先手必胜

将其分解为斐波那契数求和,从小到大排序为 r 1 , r 2 , r 3 , … , r k ( k > 1 ) r_1,r_2,r_3,\dots,r_k(k>1) r1,r2,r3,,rk(k>1)

先手第一次取走 r 1 r_1 r1 颗石子。不难发现,由于 r 1 , r 2 r_1,r_2 r1,r2 不相邻,所以 2 r 1 < r 2 2r_1<r_2 2r1<r2 (详见性质二),于是 r 2 r_2 r2 是后手所不能取完的——这就变成了与原来的游戏相似的局面。倘若石子数为斐波那契数时,后手必胜,那么本次游戏的先手一定能够取走 r 2 r_2 r2 的最后一颗。类似地,先手会取走 r 3 , … , r k r_3,\dots,r_k r3,,rk 的最后一颗,获得胜利。

必败情况

石子数是斐波那契数,则先手必败

假设石子数为 a k a_k ak ,而第一次取走了 y y y 颗。

由于 a k = a k − 2 + a k − 1 a_k=a_{k-2}+a_{k-1} ak=ak2+ak1 2 a k − 2 ≥ a k − 1 2a_{k-2}\ge a_{k-1} 2ak2ak1 ,所以 y < a k − 2 y<a_{k-2} y<ak2 (否则一招秒杀)。

那么将 a k − y a_{k}-y aky 进行斐波那契数分解,从大到小排列为 a k − 1 , a i , … , a j a_{k-1},a_{i},\dots,a_j ak1,ai,,aj

此时,如果 a j ≤ 2 y a_j\le 2y aj2y ,那么后手就可以利用上面的必胜情况的策略,直接拿完 a j a_j aj 取得胜利。

反证法,不妨设 a j > 2 y a_j>2y aj>2y ,那么 y < a j 2 ≤ a j − 1 y<\frac{a_j}{2}\le a_{j-1} y<2ajaj1 (详见性质一)。

于是 a k = ( a k − y ) + y < ( a k − 1 + a i + ⋯ + a j ) + a j − 1 a_k=(a_{k}-y)+y<(a_{k-1}+a_i+\cdots+a_j)+a_{j-1} ak=(aky)+y<(ak1+ai++aj)+aj1

观察 a i + ⋯ + a j + a j − 1 a_i+\cdots+a_j+a_{j-1} ai++aj+aj1 ,要知道, a i , … , a j a_i,\dots,a_j ai,,aj 不相邻!

所以,我们给出上界 a i + ⋯ + a j + a j − 1 ≤ a i + 1 ≤ a k − 2 a_i+\dots+a_j+a_{j-1}\le a_{i+1}\le a_{k-2} ai++aj+aj1ai+1ak2 i i i k − 1 k-1 k1 也不相邻)。

然后有 a k < a k − 1 + a k − 2 = a k a_k<a_{k-1}+a_{k-2}=a_k ak<ak1+ak2=ak 。怎么可能?自己小于自己?他又不是nan

综上

其实直接的证明还不够严谨,因为必胜情况必败情况是互相调用的。但是,放在 D A G DAG DAG 的博弈状态转移图里面,利用归纳法,就可以严谨的证明了。

代码

#include <cstdio>
#include <iostream>
#include <vector>
#include <algorithm>
#include <set>
using namespace std;
inline int readint(){
	int a = 0; char c = getchar(), f = 1;
	for(; c<'0' or c>'9'; c=getchar())
		if(c == '-') f = -f;
	for(; '0'<=c and c<='9'; c=getchar())
		a = (a<<3)+(a<<1)+(c^48);
	return a*f;
}
inline void writeint(long long x){
	if(x < 0) putchar('-'), x = -x;
	if(x > 9) writeint(x/10);
	putchar((x%10)^48);
}
# define MB template < class T >
MB void getMax(T &a,const T &b){ if(a < b) a = b; }
MB void getMin(T &a,const T &b){ if(b < a) a = b; }

set<int> s;
int main(){
	long long a = 1, b = 1;
	for(long long N=(1ll<<31); b<N; b+=a,a=b-a)
		s.insert(int(b));
	while(~scanf("%lld",&a))
		if(a == 0) break;
		else if(s.count(a)) puts("Second win");
		else puts("First win");
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值