1 条题解
-
0
思路与算法
合并会改变两个堆,所以各堆不能按独立Nim处理。记s为大小为1的堆数,m为其余堆的“石子数加1”之和。m只可能为0或至少3;m=0表示全是1,m=3表示恰有一堆2,其余是1。
必败局面恰为:当m为0或3时,s能被3整除;当m>=4时,s为偶数且m为奇数。其他情况均必胜。程序读入时统计s、m,用这两类规则输出YES或NO。m=4也适用第二类,因为4是偶数,所以一定必胜。
这个结论来自按所有合法操作分析必败态,而不是从样例猜异或规律。参数2s+m等于石子总数加非空堆数,每种操作都会使它严格减小,因此可按它归纳证明。
正确性说明
先说明所有局面可用终止性归纳。取一个1使(s,m)变成(s-1,m);合并两个1变成(s-2,m+3);合并1和大堆变成(s-1,m+1);大堆取1且仍大于1、或合并两大堆,均变成(s,m-1);把一堆2取成1则变成(s+1,m-3)。只考虑实际存在对应堆的合法操作,2s+m每步至少减少1。
m=0时只有删1、合并两个1。当s是3的倍数,这两种操作分别到模3余2的m=0态、模3余1的m=3态;均非必败。m=3时恰有一个2。若s是3的倍数,删1或2变1后,s均不再被3整除;合并两个1产生m=6,合并1和2产生m=4,这两者均为偶数m,非第二类必败态。故两种小m的必败态都不能一步到必败态。反过来,m=0且s模3为1就删1,余2就合并两个1,均到必败态;m=3且s模3为1就删1,余2就把2取成1,亦到必败态。空局面(s,m)=(0,0)包含在其中。
m>=4且s偶、m奇时,m至少5。删1使s变奇;合并两个1或1与大堆使m变偶;大堆取1仍为大堆或合并两大堆使m变偶;2变1同时使s变奇、m变偶。最后一种操作若可执行,不会落到m=0或3:总m为奇,而减去3为偶,合法结果至少4。因此全部合法后继均非必败。
剩余的大m局面:若s奇、m奇,删1就使s偶而m奇;若s奇、m偶,将1和任意大堆合并,使s偶、m奇。若s偶、m偶且m>=6,必有大小>=3的大堆或至少两个大堆,可取1或合并两大堆,使m减1,得到偶s奇m。最后m=4时只有一堆3:若s=0,把3取成2到(0,3);若s>=2,合并两个1到(s-2,7),均为必败态。因而所有非所列必败局面都有合法一步到必败态。结合严格减小的量与终态,结论成立。
复杂度
每组读取N个数并统计两个整数,初始化O(1),处理O(N),额外空间O(1)。全部测试组总时间O(T+各组N之和),不保存整局石子数组。m最大不超过50乘1001,普通int足够。
易错点
- 只可从一堆取一颗,不能任取若干;合并允许任意两堆,不要求相邻。
- m统计每个大堆的a_i+1,不是只统计总石子或大堆数。
- m=0和m=3需要模3分类;m>=4才使用奇偶规则,不能合并边界。
- 所有堆为1、恰一堆2、大小3、多个大堆及最大50堆均有覆盖。输入为正石子数;空局面只用于终态证明,不伪造N=0输入。
参考代码(C++11)
#include <iostream> using namespace std; int main() { ios::sync_with_stdio(false); cin.tie(nullptr); int T; cin >> T; while (T--) { int n, ones = 0, m = 0; cin >> n; while (n--) { int a; cin >> a; if (a == 1) ++ones; else m += a + 1; } bool win = m <= 3 ? ones % 3 != 0 : (ones % 2 != 0 || m % 2 == 0); cout << (win ? "YES" : "NO") << '\n'; } }
- 1