題意
有兩堆石子,數量分別為\(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\)
(但我不會,還沒學)
所以想學的可以去別的地方學學
最后如果以通項方式解決,通項應該也是上面推出的那個
據說還有更簡單的遞推方法
我直接問號為敬?????

最后這里放一下同校另外兩位大佬本題的博客
