每天一道博弈論之“E&D”
題意:
給你n組石子,每組有兩摞。操作為選其中一摞石子分成兩摞,拋棄原來同一組的另一摞石子,直到無法操作。問給你這n組石子,問先手勝還是後手勝。 (比如(10,8,),你可以將10分為(5,5),那麽這一組石子就成了(5,5),那個8就可以不用管了)
題目鏈接:https://www.luogu.org/problemnew/show/P2148
題解:
其實這道題我也不是很會,看了題解才寫出來T_T
首先每組是獨立的,可以求出每一組的SG值然後用SG定理解決。
那麽每組的SG值怎麽求呢?
好像沒有人會qwq 所以我們先打個表。比如下面這個10*10的表(第一行為列號)
i:0 1 2 3 4 5 6 7 8 9 10
i:1 0 1 0 2 0 1 0 3 0 1
i:2 1 1 2 2 1 1 3 3 1 1
i:3 0 2 0 2 0 3 0 3 0 2
i:4 2 2 2 2 3 3 3 3 2 2
i:5 0 1 0 3 0 1 0 3 0 1
i:6 1 1 3 3 1 1 3 3 1 1
i:7 0 3 0 3 0 3 0 3 0 4
i:8 3 3 3 3 3 3 3 3 4 4
i:9 0 1 0 2 0 1 0 4 0 1
i:10 1 1 2 2 1 1 4 4 1 1
我(bie)們(ren)可以發現這麽一個規律:
1,當i和j都是奇數時sg值為0 . 2,當i和j都為偶數是sg[i][j] = sg[i/2][j/2]+1
3,當i,j一奇一偶時(假設i為奇數)sg[i][j] = sg[i+1][j+1]
復雜度為O(t*n*logS)
於是這道題就做完啦qwq
AC代碼:
#include<iostream> #include<cstdio> #include<cstring> #define LL long long #define RI register int using namespace std; const int INF = 0x7ffffff ; const int N = 1e4 + 10 ; inlineView Codeint read() { int k = 0 , f = 1 ; char c = getchar() ; for( ; !isdigit(c) ; c = getchar()) if(c == ‘-‘) f = -1 ; for( ; isdigit(c) ; c = getchar()) k = k*10 + c-‘0‘ ; return k*f ; } int n, hh[N] ; int ser(int x,int y) { if(x%2 && y%2) return 0 ; if(x%2 &&!(y%2)) return ser(x+1,y) ; if(!(x%2) && y%2) return ser(x,y+1) ; return ser(x>>1,y>>1)+1 ; } int main() { int t = read() ; while(t--) { int cnt = 0 ; n = read() ; n >>= 1 ; for(int i=1;i<=n;i++) { int a = read(), b = read() ; hh[++cnt] = ser(a,b) ; } int ans = 0 ; for(int i=1;i<=cnt;i++) ans ^= hh[i] ; if(ans) printf("YES\n") ; else printf("NO\n") ; } return 0 ; }
打表代碼:
#include<iostream> #include<cstdio> #include<cstring> #define LL long long #define RI register int using namespace std; const int INF = 0x7ffffff ; const int N = 1000 + 10 ; inline int read() { int k = 0 , f = 1 ; char c = getchar() ; for( ; !isdigit(c) ; c = getchar()) if(c == ‘-‘) f = -1 ; for( ; isdigit(c) ; c = getchar()) k = k*10 + c-‘0‘ ; return k*f ; } int sg[N][N] ; int ser(int x,int y) { if(sg[x][y]) return sg[x][y] ; bool vis[N] = {0} ; for(int i=1;i<x;i++) vis[ser(x-i,i)] = 1 ; for(int i=1;i<y;i++) vis[ser(y-i,i)] = 1 ; for(int i=0; ;i++) if(!vis[i]) { sg[x][y] = i ; return i ; } } int main() { sg[1][1] = 0 ; int n = read() ; for(int i=1;i<=n;i++) { for(int j=1;j<=n;j++) { sg[i][j] = ser(i,j) ; } } printf("i:0 ") ; for(int i=1;i<=n;i++) if(i<10) printf("%d ",i) ; else printf("%d",i) ; printf("\n") ; // 輸出列號 for(int i=1;i<=n;i++) { if(i<10) printf("i:%d ",i) ; // 輸出行號 else printf("i:%d ",i) ; // 輸出行號(為了整齊 for(int j=1;j<=n;j++) { printf("%d ",sg[i][j]) ; } printf("\n") ; } return 0 ; }View Code
每天一道博弈論之“E&D”