题解
Nim定义:
通常的Nim游戏的定义是这样的:有若干堆石子,每堆石子的数量都是有限的,合法的移动是“选择一堆石子并拿走若干颗(不能不拿)”,如果轮到某个人时所有的石子堆都已经被拿空了,则判负(因为他此刻没有任何合法的移动)。
这游戏看上去有点复杂,先从简单情况开始研究吧。如果轮到你的时候,只剩下一堆石子,那么此时的必胜策略肯定是把这堆石子全部拿完一颗也不给对手剩,然后对手就输了。如果剩下两堆不相等的石子,必胜策略是通过取多的一堆的石子将两堆石子变得相等,以后如果对手在某一堆里拿若干颗,你就可以在另一堆中拿同样多的颗数,直至胜利。如果你面对的是两堆相等的石子,那么此时你是没有任何必胜策略的,反而对手可以遵循上面的策略保证必胜。如果是三堆石子……好像已经很难分析了,看来我们必须要借助一些其它好用的(最好是程式化的)分析方法了,或者说,我们最好能够设计出一种在有必胜策略时就能找到必胜策略的算法。
定义P-position和N-position,其中P代表Previous,N代表Next。直观的说,上一次move的人有必胜策略的局面是P-position,也就是“后手可保证必胜”或者“先手必败”,轮到move的人有必胜策略的局面是N-position,也就是“先手可保证必胜”。更严谨的定义是:1.无法进行任何移动的局面(也就是terminal position)是P-position;2.可以移动到P-position的局面是N-position;3.所有移动都导致N-position的局面是P-position。
按照这个定义,如果局面不可能重现,或者说positions的集合可以进行拓扑排序,那么每个position或者是P-position或者是N-position,而且可以通过定义计算出来。
对于先手来说,
当前局势是 P-position,必败
当前局势是 N-position,必胜
结论:
对于一个Nim游戏的局面(a1,a2,…,an),它是P-position(必败)当且仅当a1 ^ a2 ^ … ^ an=0
对于G堆来讲,遵循nim游戏,直接套用结论
对于R堆和B堆来讲,玩家肯定是一个一个拿的,
做法:先判断所有的G堆带来的局势,然后再看看R和B的数量
#include <bits/stdc++.h>
using namespace std;
const int N = 1e6 + 10;
int n, m, k;
string s;
int main() {
ios::sync_with_stdio(0);
int T;
cin >> T;
for (int cs = 1; cs <= T; ++cs) {
cin >> n;
int r = 0, g = 0, b = 0;
for (int i = 1, x; i <= n; ++i) {
cin >> s >> x;
if (s == "R") r += x;
else if (s == "G") g ^= x;
else b += x;
}
if (g) r++;//当前局势先手必胜 相当于r+1
if (r > b) puts("YES");
else puts("NO");
}
return 0;
}