牛客國慶集訓派對Day3: G. Stones(博弈+SG)
阿新 • • 發佈:2018-12-13
G. Stones
題目描述
有n堆石子,第i堆石子有xi個。 修修和棟棟輪流取石子,每人每次需要從任意一堆石子中取走個,修修先手。無法操作的人失敗。此外,如果一個人取完了一堆石子,他會立即獲勝。 不巧的是,修修除了數數以外啥都不會,他希望你幫他求出他能否獲勝。
輸入描述:
第一行一個整數t表示資料組數 (1 ≤ t ≤ 1000)。 每組資料第一行三個整數n,a,b (1 ≤ n ≤ 1000,1≤ a ≤ b ≤ 109),第二行n個整數 (1 ≤ xi ≤ 109)。
輸出描述:
每組資料輸出一行一個字串:如果修修可以獲勝輸出Yes,否則輸出No
輸入
2 1 1 3 4 1 1 3 6
輸出
No Yes
其實就是簡單的Nim博弈,求出每一堆石子的SG(),然後異或起來就好了
不過這題多了一個條件:每個人都可以取[a, b]個,但若一個人取完了一堆石子,他會立即獲勝
那麼就相當於多了三種情況:
- 如果一開始某堆石子的範圍就在[a, b]內,很顯然先手直接獲勝
- 如果某堆石子的範圍是[0, a),那麼這堆和沒有一樣
- 如果某堆石子個數大於b個,那麼在求SG時,所有≤b的狀態都不可達,因為到達就必輸
然後就是常規操作了
#include<stdio.h> #include<string.h> #include<algorithm> #include<map> #include<string> #include<math.h> #include<queue> #include<stack> #include<iostream> using namespace std; #define LL long long #define mod 1000000007 int L, R; int SG(int x) { if(x<=R) return 0; if(L==1) return x%(L+R); x -= R; x %= (L+R); x /= L; if(x<=1) x ^= 1; return x; } int main(void) { int T, n, i, x, ans, p; scanf("%d", &T); while(T--) { ans = p = 0; scanf("%d%d%d", &n, &L, &R); for(i=1;i<=n;i++) { scanf("%d", &x); if(x>=L && x<=R) p = 1; ans ^= SG(x); } if(p==1 || ans) printf("Yes\n"); else printf("No\n"); } } /*int cnt, a, b, tak[1005], Hash[1005], sg[1005]; void Getsg(int n) { int i, j; memset(sg, 0, sizeof(sg)); for(i=1;i<=n;i++) { if(i>=a && i<=b) continue; memset(Hash, 0, sizeof(Hash)); for(j=1;tak[j]<=i&&j<=cnt;j++) { if(i-tak[j]>=a && i-tak[j]<=b) continue; Hash[sg[i-tak[j]]] = 1; } for(j=0;j<=n;j++) { if(Hash[j]==0) { sg[i] = j; break; } } } } int main(void) { int i; while(scanf("%d%d", &a, &b)!=EOF) { cnt = 0; memset(tak, 0, sizeof(tak)); for(i=a;i<=b;i++) tak[++cnt] = i; Getsg(30); for(i=1;i<=30;i++) printf("%d ", sg[i]); puts(""); } return 0; } */