1. 程式人生 > 實用技巧 >HDU 6869 - Slime and Stones (威佐夫博弈)

HDU 6869 - Slime and Stones (威佐夫博弈)

HDU 6869 - Slime and Stones


題意

有兩堆石子,數量分別為\(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\)的必敗態分佈情況為

\[(1,3)\\(2,6)\\(4,10)\\(5,13)\\(7,17)\\ (8,20)\\(9,23)\\(11,27)\\(12,30)\\(14,34)\\ (15,37)\\(16,40)\\(18,44)\\(19,47)\\(21,51)\\ (22,54)\\(24,58)\\(25,61)\\(26,64) \]

觀察這個分佈,得到一個規律:

\((a,b)\)\(b-a\)值以\(2,4,6,8,10,12,14,16,18,20,\dots\)遞增

這可能是一個入手點,那麼我們把\(k=0\)的表打出來試試


\(k=0\)時,最小資料必敗態為\((1,2)\)

按規律打表如下

\[(1,2)\\(3,5)\\(4,7)\\(6,10)\\(8,13)\\ (9,15)\\(11,18)\\(12,20)\\(14,23)\\(16,26)\\ (17,28)\\(19,31)\\(21,34)\\(22,36)\\(24,39)\\ (25,41)\\(27,44) \]

得到\(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\)時,必敗態為

\[(1,4)\\(2,8)\\(3,12)\\(5,17)\\(6,21)\\ (7,25)\\(9,30)\\(10,34)\\(11,38)\\(13,43)\\ (14,47)\\(15,51)\\(16,55) \]

\(k=3\)時,必敗態為

\[(1,5)\\(2,10)\\(3,15)\\(4,20)\\(6,26)\\ (7,31)\\(8,36)\\(9,41)\\(11,47)\\(12,52)\\ (13,57)\\(14,62) \]

\(k=4\)時,必敗態為

\[(1,6)\\(2,12)\\(3,18)\\(4,24)\\(5,30)\\ (7,37)\\(8,43)\\(9,49)\\(10,55)\\(11,61)\\ (13,68)\\(14,74)\\(15,80) \]


於是我們得到了五個數列\(\{b\}\),如下

\[\{b\}= \left \{ \begin{aligned} 2,5,7,10,13,15,18,20,23,26,28,31,34,\dots (k=0)\\ 3,6,10,13,17,20,23,27,30,34,37,40,44,\dots (k=1)\\ 4,8,12,17,21,25,30,34,38,43,47,51,55,\dots (k=2)\\ 5,10,15,20,26,31,36,41,47,52,57,62,\dots (k=3)\\ 6,12,18,24,30,37,43,49,55,61,68,74,\dots (k=4) \end{aligned} \right . \]

根據OEIS的輸出,得出(需要稍微轉化一下)

\[\{b\}= \left \{ \begin{aligned} \lfloor n\times \frac{3+\sqrt 5}{2} \rfloor,\ k=0\\ \lfloor n\times \frac{4+\sqrt 8}{2} \rfloor,\ k=1\\ \lfloor n\times \frac{5+\sqrt {13}}{2} \rfloor,\ k=2\\ \lfloor n\times \frac{6+\sqrt {20}}{2} \rfloor,\ k=3\\ \lfloor n\times \frac{7+\sqrt {29}}{2} \rfloor,\ k=4\\ \end{aligned} \right . \]

容易發現規律,並得到通項表達如下

\[b_k=\lfloor n\times \frac{x+\sqrt y}{2} \rfloor\\ x=k+3\\ y=5+\frac{3+(k\times 2+1)}{2}\times k \]

\(y\)\({5,8,13,20,29,\dots}\),每項差\(3,5,7,9,\dots\),可以拆成\(5+\)等差數列求和)



程式

(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;
}
/*
10
15 37 1
26 64 1
21 51 1
9 23 1
25 41 0
27 44 0
14 62 3
20 105 4
19 99 4
8 36 3
*/



原版博弈

本題是威佐夫博弈(Wythoff's game)的變種

原版威佐夫博弈正是本題\(k=0\)時的情況

結論是:

假設兩堆石子數量為\((x,y),\ x\lt y\)

先手必敗,當且僅當滿足\(\frac{\sqrt 5+1}{2}(y-x)=x\)

(但我不會,還沒學)

所以想學的可以去別的地方學學

最後如果以通項方式解決,通項應該也是上面推出的那個




據說還有更簡單的遞推方法

我直接問號為敬?????