sg函式小結

2022-05-07 21:00:10 字數 3750 閱讀 9353

sg函式是處理博弈問題的重要工具。

我們知道sg(x)=mex

sg(x)=0時代表後手贏,否則先手贏。

對於乙個問題,如果某些子問題是相互獨立的,我們就可以用sg定理,總問題的sg等於各個子問題的異或和。

看幾道題:

任何乙個大學生對菲波那契數列(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、 最先取光所有石子的人為勝者;

假設雙方都使用最優策略,請判斷先手的人會贏還是後手的人會贏。

輸入資料報含多個測試用例,每個測試用例佔一行,包含3個整數m,n,p(1<=m,n,p<=1000)。

m=n=p=0則表示輸入結束。

如果先手的人能贏,請輸出「fibo」,否則請輸出「nacci」,每個例項的輸出佔一行。

一道暴力算sg函式的題

#includeusing namespace std;

#define rep(i,st,ed) for(register int i=st,i##end=ed;i<=i##end;++i)

#define drep(i,st,ed) for(register int i=st,i##end=ed;i>=i##end;--i)

typedef long long ll;

inline int read()

inline ll readll()

const int maxn=1000+10;

int f[maxn],fib[maxn],tmp;

bool p[maxn];

int main()

--tmp;

rep(i,1,1000)

inline ll readll()

int getsg(int x,int y)

int main()

if(sum) printf("yes\n");

else printf("no\n");

}return 0;

}

題意:有n堆石子,每堆石子有\(a_i\)個,有乙個數\(k_i\)現在兩個人博弈,每個人可以拿掉一堆石子裡\(1\)~\(\lfloor \frac \rfloor\)數量的石子,x為當前這一堆石子的數量,誰不能拿就輸了,求誰贏。

資料範圍:

\(1≤n≤200\)

\(1≤a_i,k_i≤10^9\)

又是找規律。。k=2的情況是石子遊戲。規律也有些相似

通過打表發現

當x%k=0時\(sg(x)=x/k\)

\(sg(x)=sg(x-\lfloor \frac \rfloor + 1)\)

所以我們可以遞迴求sg函式,但是這樣會t

我們\(\lfloor \frac \rfloor + 1\)相同的放在一次一起處理就可以了

#includeusing namespace std;

#define rep(i,st,ed) for(register int i=st,i##end=ed;i<=i##end;++i)

#define drep(i,st,ed) for(register int i=st,i##end=ed;i>=i##end;--i)

typedef long long ll;

inline int read()

inline ll readll()

const int maxn=1e5+10;

int main()

inline ll readll()

const int maxn=30;

int f[maxn],a[maxn];

bool p[10000+10];

void init(int n)

}}int main()

if(!ans) printf("-1 -1 -1\n");

printf("%d\n",ans);

}return 0;

}

有乙個長度為n的陣列,甲乙兩人在上面進行這樣乙個遊戲:首先,陣列上有一些格仔是白的,有一些是黑的。然

後兩人輪流進行操作。每次操作選擇乙個白色的格仔,假設它的下標為x。接著,選擇乙個大小在1~n/x之間的整數

k,然後將下標為x、2x、...、kx的格仔都進行顏色翻轉。不能操作的人輸。現在甲(先手)有一些詢問。每次他

會給你乙個陣列的初始狀態,你要求出對於這種初始狀態他是否有必勝策略。

接下來2*k行,每兩行表示一次詢問。在這兩行中,第一行乙個正整數w,表示陣列中有多少個格仔是白色的,第二

行則有w個1~n之間的正整數,表示白色格仔的對應下標。

對於每個詢問,若先手必勝輸出"yes",否則輸出"no"。答案之間用換行隔開

$,n<=1000000000 , k,w<=100 $, 不會有格仔在同一次詢問中多次出現。

考慮可以把問題轉化為既可以選白點,又可以選黑點。因為如果選黑點,一定不能一次勝利,而反倒對對方有利,且對方可以選一樣的將狀態重置。

這樣\(sg(x)=mex\\)

這樣時間複雜度還是不能過。

我們可以發現乙個性質:

當 $ \lfloor \frac \rfloor $ = $ \lfloor \frac \rfloor $ 時 $ sg( i ) = sg( j ) $,可以用歸納法證明

之後我們就可以分塊,相同的塊放在一起。

記錄答案時,大於\(\sqrt\)的直接記錄在$ \lfloor \frac \rfloor $上就可以了

#includeusing namespace std;

#define rep(i,st,ed) for(register int i=st,i##end=ed;i<=i##end;++i)

#define drep(i,st,ed) for(register int i=st,i##end=ed;i>=i##end;--i)

typedef long long ll;

inline int read()

inline ll readll()

const int maxn=1e5+10;

int lim;

int n,f[maxn],g[maxn];

bool p[maxn];

int a[maxn];

inline int sg(int x)

void init()

int t=1;

while(p[t]) ++t;

if(i>lim) f[n/i]=t;

else g[i]=t;

rep(i,1,res) p[a[i]]=0;

}}int main()

if(ans) printf("yes\n");

else printf("no\n");

}return 0;

}

SG函式和SG定理

大神鏈 我們先用乙個簡單的例子引入 考慮有這樣乙個遊戲,有3堆火柴,分別有a,b,c根,記為 a,b,c 每次乙個遊戲者可以從任意一堆中拿走至少一根火柴,也可以整堆拿走,但是不能從多堆火柴中拿 無法拿火柴的人輸 這個遊戲就叫做nim遊戲 舉個例子,假設a 1,b 2,c 3,若你先拿,你會怎麼辦呢?...

sg函式和sg定理

下面這篇解釋得很清楚。單個遊戲 sg 0 0 sg值為0的有且只有必敗態 sg x mex sg y x的所有後繼狀態中 第乙個不能達到的sg值,則x可到達sg為0 sg x 1的狀態 組合遊戲 可將sg值看作為石子數,則轉化為nim取石子遊戲,總遊戲的勝負由sg的異或和決定。hdu1848 1 i...

freopen函式 SG函式與SG定理

sg函式是用來解決acm競賽中公平組合遊戲問題的利器 在介紹sg函式和sg定理之前我們先介紹介紹必勝點與必敗點.必勝點和必敗點的概念 p點 必敗點,換而言之,就是誰處於此位置,則在雙方操作正確的情況下必敗。n點 必勝點,處於此情況下,雙方操作均正確的情況下必勝。必勝點和必敗點的性質 1 所有終結點是...