Problem H. Cups and Beans 2017.8.11
原题:
There are N cups numbered 0 through N − 1. For each i(1 ≤ i ≤ N − 1), the cup i contains Ai beans,
and this cup is labeled with an integer Ci.
Two people will play the following game:
• In each turn, the player chooses a bean from one of the cups except for the cup 0.
• If he chooses a bean from the cup i, he must move it to one of the cups i − Ci;…… ; i − 1.
• The players take turns alternately. If a player can’t choose a bean, he loses.
Who will win if both players play optimally?
Input:
Input Format:
N
C(1) A(1)
C(2) A(2)
…
C(N−1) A(N−1)
Constraints:
• 2 ≤ N ≤ 105
• 1 ≤ Ci ≤ i
• 0 ≤ Ai ≤ 109
• At least one of Ai is nonzero.
• All values in the input are integers.
Output:
Print the name of the winner: \First” or \Second”.
Examples:
standard input | standard output |
3 1 0 1 1 |
Second |
7 1 1 2 0 1 0 2 0 4 1 3 0 |
First |
7 1 1 2 0 1 9 2 10 4 3 3 5 |
Second |
Note:
Notes to the Sample 1:
• In the first turn, the first player must move a bean from 2 to 1.
• In the second turn, the second player must move a bean from 1 to 0.
• In the third turn, the first player can’t choose a bean and loses.
题目大意:
典型的博弈论,N个瓶子,第一个瓶子里没有石子,后面N-1个瓶子里分别有A[i]个石子,每个瓶子上贴了标签C[i],表示每次操作仅可以将这个瓶子里的一个石子石子最多可以移动到前面C[i]个瓶子里(也就是只可以将该瓶子里的一个石子放到 i-C[i] 到 i-1 号瓶子里),最后不能进行移动的人输,求谁赢。
解法分析:
可以将这个问题转化成限定条件的Nim取石子问题,题目的操作是将高位瓶子的石子移动到低位瓶子里,那么石子所在的瓶子标号其实就可以认为是石子堆的石子数,对于每个特定数量的石子堆 i,都有一个限定条件 C[i] 表示最多取i个,A[i] 则表示石子数为i的石子堆有A[i]个。
知道了题意,就可以直接暴力了,枚举前面的 i-C[i] 到 i – 1 的状态然后递推。可是数据范围是1e5,这样做无疑会超时,在周大爷的敏锐观察下,套用了一个主席树维护mex数的模板,就过了。
代码:
#include#define MAXN 100005 using namespace std; typedef long long LL; #define lc(x) t[x].l #define rc(x) t[x].r const int N = 2e5 + 5, MX = 1e9 + 5; struct node { int l, r, mn; } t[N * 30]; int sz, root[N]; void ins(int &x, int l, int r, int p, int v) {//right of p is v t[++sz] = t[x]; x = sz; if (l == r) t[x].mn = v; else { int mid = (l + r) >> 1; if (p <= mid) ins(t[x].l, l, mid, p, v); else ins(t[x].r, mid + 1, r, p, v); t[x].mn = min(t[lc(x)].mn, t[rc(x)].mn); } } int query(int x, int l, int r, int v) { if (l == r) return l; else { int mid = (l + r) >> 1; if (t[lc(x)].mn < v) return query(t[x].l, l, mid, v); else return query(t[x].r, mid + 1, r, v); } } int SG[100005], n; bool vis[100005]; int C[100005], A[100005]; int main() { int i, j, k; scanf("%d", &n); for (i = 1; i < n; i++) { scanf("%d%d", &C[i + 1], &A[i + 1]); } SG[1] = 0; ins(root[1],0,MX,SG[0],1);//ins是插入操作,root[1]表示当前树,0表示最小值,MX表示最大值,SG[0]是值,1是位置 for (i = 2; i <= n; i++) { int l = max(i - C[i], 1), r = i - 1; SG[i] = query(root[r], 0, MX, l); root[i] = root[i-1]; ins(root[i],0,MX,SG[i],i); } // for(int i = 1;i <= n;i++) printf("%d ",SG[i]); int ans = 0; for (i = 2; i <= n; i++) { if (A[i] % 2 == 1) ans ^= SG[i]; } if (ans) printf("First\n"); else printf("Second\n"); return 0; }