题意
有两堆石子,数量分别为\(a,b\),两人轮流取,每次必须取一个及以上的石子
每次可以任选一堆取(个数不限),也可以两堆都取(取的个数差值必须\(\leq k\))
两人都以最优策略取石子,问是否存在一种情况使得先手必胜,是则输出\(1\),否则输出\(0\)
限制
\(1\leq T\leq 10^5\)
\(1\leq a,b\leq 10^8,\ 0\leq k\leq 10^8\)
赛时思路
这部分解法可能是现场赛几乎不可能实现的写法吧
- 下文令\((x,y)\)表示现在两堆石子的数量,限制\(x\leq y\)
首先考虑最小数据的必败态
根据题意可以得知,谁将某一堆石子取完,那么他的对手就可以取完另外一堆,所以这种方式必败
或者如果两堆石子数量差在\(k\)之内(\(|a-b|\leq k\)),那么就可以直接拿走两堆内所有石子并获胜
所以可以得到,如果较少的堆内剩余\(1\)粒石子,较多的堆内剩余\(k+2\)粒,那么当前不论怎么操作都会输掉这场比赛
得到:\((1,k+2)\)为最小数据下的必败态
为了找到规律,我们枚举两堆石子中较少堆的数量
当较少的堆中仅存在\(1\)粒石子时——
当剩余情况为\((1,i),\ 1\leq i\leq k+1\)时,当前操作的人可以直接拿完两堆,所以必胜
当剩余情况为\((1,k+2)\)时,必败
当剩余情况为\((1,i),\ i\geq k+3\)时,当前操作的人可以将其转换到\((1,k+2)\)的情况,所以必胜
相同的,当较少的堆中仅存在\(2\)粒石子时——(这里不适用于\(k=0\)的情况)
当剩余情况为\((2,i),\ 2\leq i\leq k+2\)时,当前操作的人可以直接拿完两堆,所以必胜
当剩余情况为\((2,i),\ k+3\leq i\leq 2k+3\),将较少的堆拿去\(1\)粒,较大的堆最多可以拿\(k+1\)粒,所以可以转移到\((1,k+2)\)的状态,所以必胜
当剩余情况为\((2,2k+4)\)时,必败
当剩余情况为\((2,i),\ i\geq 2k+5\)时,当前操作的人可以将其转换到\((2,2k+4)\)的情况,所以必胜
直到较少的堆内存在\(k+2\)粒石子时,情况发生了变化——
当剩余情况为\((k+2,i),\ k+2\leq i\leq 2k+3\)时,当前操作的人可以直接拿完两堆,必胜
当剩余情况为\((k+2,i),\ i\geq 2k+4\)时,发现前面有个必败态为\((1,k+2)\),他们共享\(k+2\)这个状态,所以可以从\((k+2,2k+4)\)直接转换到\((1,k+2)\)的状态,所以该情况下必胜
综上,可以得到的一个规律就是
后面的一个必败态总是可以由前一个必败态推导而来
假设\((x,y)\)是一个必败态,假设较小的堆被拿了\(1\)粒石子,那么较大的堆最多能拿\(k+1\)粒石子
显然,\((x+1,y+k+2)\)无法仅通过一步就推到\((x,y)\),可能必败
还要考虑一点,就是\(x+1\)没有在前面的任意必败态中出现过,否则可以直接一步转移到更前面的必败态,使得该点必胜
如果\(x+1\)并未出现在前面任意一个必败态中,就可以肯定\((x+1,y+k+2)\)无法转移到任意一个必败态,则它不是必胜态,是一个必败态
以\(k=1\)的情况为例,最小必败态为\((1,3)\)
考虑\((x+1,y+k+2)\)的转移方式,显然\(x+1=2\)并未出现在前面任意一个必败态中
所以\((2,6)\)是一个必败态
继续考虑,发现下一个状态为\((3,9)\)
但是\(3\)存在于必败态\((1,3)\)内,说明\((3,9)\)可以直接转换到\((1.3)\),这是个必胜态
为了让其不能一遍转移得到,则可以假设两堆都多取了一粒石子,即考虑\((x+1+1,y+k+2+1)\)
发现\(2+1+1=4\)并未出现,所以\((4,10)\)是一个必败态
一直这样考虑下去,粗略得到\(k=1\)的必败态分布情况为
观察这个分布,得到一个规律:
\((a,b)\)的\(b-a\)值以\(2,4,6,8,10,12,14,16,18,20,\dots\)递增
这可能是一个入手点,那么我们把\(k=0\)的表打出来试试
当\(k=0\)时,最小数据必败态为\((1,2)\)
按规律打表如下
得到\(b-a\)的值以\(1,2,3,4,5,6,7,\dots\)递增
可以得到,\(b-a\)的值是一个首项为\(k+1\),公差为\(k+1\)的等差数列
所以我们可以根据\(\frac {b-a}{k+1}\)来确定某个状态在必败态中的项数
如果你想问为什么要求出项数,看下面……
实际上打出表就能发现
必败态的\(b\)数值分布存在一个规律(\(b_i-b_{i-1}\in\{k+2,k+3\}\))
假如这个\(\{b\}\)数列存在着通项公式,那么肯定是形如\(b_i=\lfloor i\times k\rfloor,\ k\in\R\)
这样才能保证前后两项差值固定在一个集合内(虽然这个常数\(k\)可能很难表示)
那么我们就大胆着手于找通项公式
这里开始往下应该也可以采用线性递推模板解决,不过我没试过,不确定会不会被卡
打开OEIS,准备尝试玄学求出可能的通项(这是个能根据数列前几项或者中间几项求出通项公式的工具)
但不同的\(k\)肯定对应着不同的通项公式,所以我们再把\(k=2,3,4\)的表稍微打出前几项
当\(k=2\)时,必败态为
当\(k=3\)时,必败态为
当\(k=4\)时,必败态为
于是我们得到了五个数列\(\{b\}\),如下
根据OEIS的输出,得出(需要稍微转化一下)
容易发现规律,并得到通项表达如下
(\(y\)为\({5,8,13,20,29,\dots}\),每项差\(3,5,7,9,\dots\),可以拆成\(5+\)等差数列求和)
得到了\(\{b\}\)数列的通项,那该怎么判断对应的是哪一项,是不是必败态呢
前面提到,我们能够根据\(b-a\)的值获得项数为\(\frac{b-a}{k+1}\)
根据项数代入公式即可求出必败态这一项的\(b\)
判断下两个\(b\)是不是相同的就能确定是不是必败态了
程序
(78ms/1000ms)
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
void solve()
{
ll a,b,k;
scanf("%lld%lld%lld",&a,&b,&k);
if(a>b)
swap(a,b);
if((b-a)%(k+1)==0)
{
int id=(b-a)/(k+1);
ll x=3+k,y=(3+k*2+1)*k/2+5;
ll tmp=(x+sqrt(y))*id/2.0;
if(tmp==b)
puts("0");
else
puts("1");
}
else
puts("1");
}
int main()
{
int T;
scanf("%d",&T);
while(T--)
solve();
return 0;
}
原版博弈
本题是威佐夫博弈(Wythoff's game)的变种
原版威佐夫博弈正是本题\(k=0\)时的情况
结论是:
假设两堆石子数量为\((x,y),\ x\lt y\)
先手必败,当且仅当满足\(\frac{\sqrt 5+1}{2}(y-x)=x\)
(但我不会,还没学)
所以想学的可以去别的地方学学
最后如果以通项方式解决,通项应该也是上面推出的那个
据说还有更简单的递推方法
我直接问号为敬?????
最后这里放一下同校另外两位大佬本题的博客