891. Nim游戏
给定 n
堆石子,两位玩家轮流操作,每次操作可以从任意一堆石子中拿走任意数量的石子(可以拿完,但不能不拿),最后无法进行操作的人视为失败。
问如果两人都采用最优策略,先手是否必胜。
输入格式
第一行包含整数 n
。
第二行包含 n
个数字,其中第 i 个数字表示第 i
堆石子的数量。
输出格式
如果先手方必胜,则输出 Yes
。
否则,输出 No
。
数据范围
1≤n≤105
,
1≤每堆石子数≤109
输入样例:
2
2 3
输出样例:
Yes
* 假定石堆的数量是:S1,S2,...,Si,Sj,...,Sn;
* 1)如果S1^S2^...^Si^Sj^...^Sn == 0 ,那么必然存在先手必败;
* 证明:如果S1^S2^...^Si^Sj^...^Sn == 0 ,那么在Si中取出一个数 x(>0) 后,
* 必然满足 S1^S2^...^(Si-x)^Sj^...^Sn != 0 ;
* 用反证法证明:假设存在 S1^S2^...^(Si-x)^Sj^...^Sn == 0,
* 因为 S1^S2^...^Si^Sj^...^Sn == 0 ,所以 S1^S2^...^Sj^...^Sn == Si;
* 所以S1^S2^...^(Si-x)^Sj^...^Sn == 0 => Si^(Si-x)==0 => Si=Si-x => x=0(与条
* 件矛盾),不成立;
* 即:如果S1^S2^...^Si^Sj^...^Sn == 0 ,那么在Si中取出一个数 x(>0) 后,
* 必然满足 S1^S2^...^(Si-x)^Sj^...^Sn != 0 ;得证。
*
* 2)如果S1^S2^...^Si^Sj^...^Sn != 0 ,那么必然存在先手必胜;
* 证明:如果S1^S2^...^Si^Sj^...^Sn !=0 ==m,假设m用二进制表示的1的最高位是第k位,
* 那么至少存在一个数字Si,使得Si用二进制表示的数的第k位是1,取出一个数 Si-(Si^m)
* 能使得 S1^S2^...^(Si-(Si-(Si^m))^Sj^...^Sn == 0;
* 证明:取出一个数 Si-(Si^m),那么 S1^S2^...^(Si-(Si-(Si^m))^Sj^...^Sn == 0;
* 所以:S1^S2^...^(Si-(Si-(Si^m))^Sj^...^Sn == 0
* => S1^S2^...^(Si^m)^Sj^...^Sn == 0
* => 将m提在后面: S1^S2^...^Si^Sj^...^Sn^m == 0
* 因为 S1^S2^...^Si^Sj^...^Sn == m, m^m ==0
* 所以得到 S1^S2^...^Si^Sj^...^Sn^m = m^m = 0;
* 现在来证明 Si-(Si^m) > 0;
* 证明:因为m的最高位1是在第k位,而Si的第k位一定存在1,所以Si^m之后,第k位的结果
* 一定是0;
* 所以在最极端的情况下, Si 与 Si^m 的比较:
* Si Si^m
* 第k位 1 0
* 第k位前面的位置 1/0 与Si相同
* 第k位后面的位置 1/0 不确定(但即便是 Si 取全0,Si^m取全1,两个数相
* 减也一定满足大于0);
* 得证!
* 得证!
/**
* 假定石堆的数量是:S1,S2,...,Si,Sj,...,Sn;
* 1)如果S1^S2^...^Si^Sj^...^Sn == 0 ,那么必然存在先手必败;
* 证明:如果S1^S2^...^Si^Sj^...^Sn == 0 ,那么在Si中取出一个数 x(>0) 后,
* 必然满足 S1^S2^...^(Si-x)^Sj^...^Sn != 0 ;
* 用反证法证明:假设存在 S1^S2^...^(Si-x)^Sj^...^Sn == 0,
* 因为 S1^S2^...^Si^Sj^...^Sn == 0 ,所以 S1^S2^...^Sj^...^Sn == Si;
* 所以S1^S2^...^(Si-x)^Sj^...^Sn == 0 => Si^(Si-x)==0 => Si=Si-x => x=0(与条
* 件矛盾),不成立;
* 即:如果S1^S2^...^Si^Sj^...^Sn == 0 ,那么在Si中取出一个数 x(>0) 后,
* 必然满足 S1^S2^...^(Si-x)^Sj^...^Sn != 0 ;得证。
*
* 2)如果S1^S2^...^Si^Sj^...^Sn != 0 ,那么必然存在先手必胜;
* 证明:如果S1^S2^...^Si^Sj^...^Sn !=0 ==m,假设m用二进制表示的1的最高位是第k位,
* 那么至少存在一个数字Si,使得Si用二进制表示的数的第k位是1,取出一个数 Si-(Si^m)
* 能使得 S1^S2^...^(Si-(Si-(Si^m))^Sj^...^Sn == 0;
* 证明:取出一个数 Si-(Si^m),那么 S1^S2^...^(Si-(Si-(Si^m))^Sj^...^Sn == 0;
* 所以:S1^S2^...^(Si-(Si-(Si^m))^Sj^...^Sn == 0
* => S1^S2^...^(Si^m)^Sj^...^Sn == 0
* => 将m提在后面: S1^S2^...^Si^Sj^...^Sn^m == 0
* 因为 S1^S2^...^Si^Sj^...^Sn == m, m^m ==0
* 所以得到 S1^S2^...^Si^Sj^...^Sn^m = m^m = 0;
* 现在来证明 Si-(Si^m) > 0;
* 证明:因为m的最高位1是在第k位,而Si的第k位一定存在1,所以Si^m之后,第k位的结果
* 一定是0;
* 所以在最极端的情况下, Si 与 Si^m 的比较:
* Si Si^m
* 第k位 1 0
* 第k位前面的位置 1/0 与Si相同
* 第k位后面的位置 1/0 不确定(但即便是 Si 取全0,Si^m取全1,两个数相
* 减也一定满足大于0);
* 得证!
* 得证!
*/
#include <iostream>
using namespace std;
int main()
{
int n;
cin >> n;
int res=0;
while (n -- )
{
int val;
cin >> val;
res^=val;
}
if(res)
puts("Yes");
else
puts("No");
return 0;
}
893. 集合-Nim游戏
给定 n
堆石子以及一个由 k 个不同正整数构成的数字集合 S
。
现在有两位玩家轮流操作,每次操作可以从任意一堆石子中拿取石子,每次拿取的石子数量必须包含于集合 S
,最后无法进行操作的人视为失败。
问如果两人都采用最优策略,先手是否必胜。
输入格式
第一行包含整数 k
,表示数字集合 S
中数字的个数。
第二行包含 k
个整数,其中第 i 个整数表示数字集合 S 中的第 i 个数 si
。
第三行包含整数 n
。
第四行包含 n
个整数,其中第 i 个整数表示第 i 堆石子的数量 hi
。
输出格式
如果先手方必胜,则输出 Yes
。
否则,输出 No
。
数据范围
1≤n,k≤100
,
1≤si,hi≤10000
输入样例:
2
2 5
3
2 4 7
输出样例:
Yes
/**
1.Mex运算:
设S表示一个非负整数集合.定义mex(S)为求出不属于集合S的最小非负整数运算,即:
mes(S)=min{x};
例如:S={0,1,2,4},那么mes(S)=3;
2.SG函数
在有向图游戏中,对于每个节点x,设从x出发共有k条有向边,分别到达节点y1,y2,····yk,
定义SG(x)的后记节点y1,y2,····yk的SG函数值构成的集合在执行mex运算的结果,即:
SG(x)=mex({SG(y1),SG(y2)····SG(yk)})
特别地,整个有向图游戏G的SG函数值被定义为有向图游戏起点s的SG函数值,即 SG(G)=SG(s).
3.有向图游戏的和
设G1,G2,····,Gm是m个有向图游戏.定义有向图游戏G,他的行动规则是任选某个有向图游戏Gi,并在Gi上行动一步.G被称为有向图游戏G1,G2,·····,Gm的和.
有向图游戏的和的SG函数值等于它包含的各个子游戏SG函数的异或和,即:
SG(G)=SG(G1)xorSG(G2)xor···xor SG(Gm)
以上定义是转载了一个dl的:
作者:E.lena
链接:https://www.acwing.com/solution/content/23435/
来源:AcWing
只要得到所有石堆的SG值,那么就和 Nim 游戏操作一样,将每堆石子的SG值当作 Nim游戏的
每堆石堆的数量,那么每次都可以取连接该集合的任一个方向,单是要得到最优策略,还是要
根据所有石堆的异或和来选择特定石堆的数量;
给出Nim游戏的证明后再探讨Nim集合的正确性:
* 假定石堆的数量是:S1,S2,...,Si,Sj,...,Sn;
* 1)如果S1^S2^...^Si^Sj^...^Sn == 0 ,那么必然存在先手必败;
* 证明:如果S1^S2^...^Si^Sj^...^Sn == 0 ,那么在Si中取出一个数 x(>0) 后,
* 必然满足 S1^S2^...^(Si-x)^Sj^...^Sn != 0 ;
* 用反证法证明:假设存在 S1^S2^...^(Si-x)^Sj^...^Sn == 0,
* 因为 S1^S2^...^Si^Sj^...^Sn == 0 ,所以 S1^S2^...^Sj^...^Sn == Si;
* 所以S1^S2^...^(Si-x)^Sj^...^Sn == 0 => Si^(Si-x)==0 => Si=Si-x => x=0(与条
* 件矛盾),不成立;
* 即:如果S1^S2^...^Si^Sj^...^Sn == 0 ,那么在Si中取出一个数 x(>0) 后,
* 必然满足 S1^S2^...^(Si-x)^Sj^...^Sn != 0 ;得证。
*
* 2)如果S1^S2^...^Si^Sj^...^Sn != 0 ,那么必然存在先手必胜;
* 证明:如果S1^S2^...^Si^Sj^...^Sn !=0 ==m,假设m用二进制表示的1的最高位是第k位,
* 那么至少存在一个数字Si,使得Si用二进制表示的数的第k位是1,取出一个数 Si-(Si^m)
* 能使得 S1^S2^...^(Si-(Si-(Si^m))^Sj^...^Sn == 0;
* 证明:取出一个数 Si-(Si^m),那么 S1^S2^...^(Si-(Si-(Si^m))^Sj^...^Sn == 0;
* 所以:S1^S2^...^(Si-(Si-(Si^m))^Sj^...^Sn == 0
* => S1^S2^...^(Si^m)^Sj^...^Sn == 0
* => 将m提在后面: S1^S2^...^Si^Sj^...^Sn^m == 0
* 因为 S1^S2^...^Si^Sj^...^Sn == m, m^m ==0
* 所以得到 S1^S2^...^Si^Sj^...^Sn^m = m^m = 0;
* 现在来证明 Si-(Si^m) > 0;
* 证明:因为m的最高位1是在第k位,而Si的第k位一定存在1,所以Si^m之后,第k位的结果
* 一定是0;
* 所以在最极端的情况下, Si 与 Si^m 的比较:
* Si Si^m
* 第k位 1 0
* 第k位前面的位置 1/0 与Si相同
* 第k位后面的位置 1/0 不确定(但即便是 Si 取全0,Si^m取全1,两个数相
* 减也一定满足大于0);
* 得证!
* 得证!
因为我们在Nim游戏的证明中证明出了:
如果S1^S2^...^Si^Sj^...^Sn !=0 ==m,假设m用二进制表示的1的最高位是第k位,
那么至少存在一个数字Si,使得Si用二进制表示的数的第k位是1,取出一个数 Si-(Si^m)
能使得 S1^S2^...^(Si-(Si-(Si^m))^Sj^...^Sn == 0;且Si-(Si^m) > 0,所以在Nim集合中,
我们能得出,SG1^SG2^...^SGi^...^SGn !=0 ==m 的时候,我们可以选择第i个集合里选择第
SGi-(SGi^m) 条路线,(此路线一定存在,因为SGi-(SGi^m)<SGi,而[0,SGi)的路线都存在,
所以必定存在 SGi-(SGi^m) 属于 [0,SGi);
SG1^SG2^...^SGi^...^SGn == 0,就不消证明,先手必输;
*/
/**
1.Mex运算:
设S表示一个非负整数集合.定义mex(S)为求出不属于集合S的最小非负整数运算,即:
mes(S)=min{x};
例如:S={0,1,2,4},那么mes(S)=3;
2.SG函数
在有向图游戏中,对于每个节点x,设从x出发共有k条有向边,分别到达节点y1,y2,····yk,
定义SG(x)的后记节点y1,y2,····yk的SG函数值构成的集合在执行mex运算的结果,即:
SG(x)=mex({SG(y1),SG(y2)····SG(yk)})
特别地,整个有向图游戏G的SG函数值被定义为有向图游戏起点s的SG函数值,即 SG(G)=SG(s).
3.有向图游戏的和
设G1,G2,····,Gm是m个有向图游戏.定义有向图游戏G,他的行动规则是任选某个有向图游戏Gi,并在Gi上行动一步.G被称为有向图游戏G1,G2,·····,Gm的和.
有向图游戏的和的SG函数值等于它包含的各个子游戏SG函数的异或和,即:
SG(G)=SG(G1)xorSG(G2)xor···xor SG(Gm)
以上定义是转载了一个dl的:
作者:E.lena
链接:https://www.acwing.com/solution/content/23435/
来源:AcWing
只要得到所有石堆的SG值,那么就和 Nim 游戏操作一样,将每堆石子的SG值当作 Nim游戏的
每堆石堆的数量,那么每次都可以取连接该集合的任一个方向,单是要得到最优策略,还是要
根据所有石堆的异或和来选择特定石堆的数量;
给出Nim游戏的证明后再探讨Nim集合的正确性:
* 假定石堆的数量是:S1,S2,...,Si,Sj,...,Sn;
* 1)如果S1^S2^...^Si^Sj^...^Sn == 0 ,那么必然存在先手必败;
* 证明:如果S1^S2^...^Si^Sj^...^Sn == 0 ,那么在Si中取出一个数 x(>0) 后,
* 必然满足 S1^S2^...^(Si-x)^Sj^...^Sn != 0 ;
* 用反证法证明:假设存在 S1^S2^...^(Si-x)^Sj^...^Sn == 0,
* 因为 S1^S2^...^Si^Sj^...^Sn == 0 ,所以 S1^S2^...^Sj^...^Sn == Si;
* 所以S1^S2^...^(Si-x)^Sj^...^Sn == 0 => Si^(Si-x)==0 => Si=Si-x => x=0(与条
* 件矛盾),不成立;
* 即:如果S1^S2^...^Si^Sj^...^Sn == 0 ,那么在Si中取出一个数 x(>0) 后,
* 必然满足 S1^S2^...^(Si-x)^Sj^...^Sn != 0 ;得证。
*
* 2)如果S1^S2^...^Si^Sj^...^Sn != 0 ,那么必然存在先手必胜;
* 证明:如果S1^S2^...^Si^Sj^...^Sn !=0 ==m,假设m用二进制表示的1的最高位是第k位,
* 那么至少存在一个数字Si,使得Si用二进制表示的数的第k位是1,取出一个数 Si-(Si^m)
* 能使得 S1^S2^...^(Si-(Si-(Si^m))^Sj^...^Sn == 0;
* 证明:取出一个数 Si-(Si^m),那么 S1^S2^...^(Si-(Si-(Si^m))^Sj^...^Sn == 0;
* 所以:S1^S2^...^(Si-(Si-(Si^m))^Sj^...^Sn == 0
* => S1^S2^...^(Si^m)^Sj^...^Sn == 0
* => 将m提在后面: S1^S2^...^Si^Sj^...^Sn^m == 0
* 因为 S1^S2^...^Si^Sj^...^Sn == m, m^m ==0
* 所以得到 S1^S2^...^Si^Sj^...^Sn^m = m^m = 0;
* 现在来证明 Si-(Si^m) > 0;
* 证明:因为m的最高位1是在第k位,而Si的第k位一定存在1,所以Si^m之后,第k位的结果
* 一定是0;
* 所以在最极端的情况下, Si 与 Si^m 的比较:
* Si Si^m
* 第k位 1 0
* 第k位前面的位置 1/0 与Si相同
* 第k位后面的位置 1/0 不确定(但即便是 Si 取全0,Si^m取全1,两个数相
* 减也一定满足大于0);
* 得证!
* 得证!
因为我们在Nim游戏的证明中证明出了:
如果S1^S2^...^Si^Sj^...^Sn !=0 ==m,假设m用二进制表示的1的最高位是第k位,
那么至少存在一个数字Si,使得Si用二进制表示的数的第k位是1,取出一个数 Si-(Si^m)
能使得 S1^S2^...^(Si-(Si-(Si^m))^Sj^...^Sn == 0;且Si-(Si^m) > 0,所以在Nim集合中,
我们能得出,SG1^SG2^...^SGi^...^SGn !=0 ==m 的时候,我们可以选择第i个集合里选择第
SGi-(SGi^m) 条路线,(此路线一定存在,因为SGi-(SGi^m)<SGi,而[0,SGi)的路线都存在,
所以必定存在 SGi-(SGi^m) 属于 [0,SGi);
SG1^SG2^...^SGi^...^SGn == 0,就不消证明,先手必输;
*/
#include <iostream>
#include <set>
#include <cstring>
using namespace std;
const int maxn = 10010;
int hs[maxn],f[maxn];
int m;
int SG(int val)
{
if(hs[val]!=-1) //如果val块石子的石堆处理过,直接返回
return hs[val];
set<int> st;
for(int i=0;i<m;++i)
{
int sum=f[i];
if(val>=sum)
st.insert(SG(val-sum));
}
for(int i=0; ;++i)
if(!st.count(i)) //只要有最小不等于该集合的非负整数,就赋给hs[val];
return hs[val]=i;
}
int main()
{
cin >> m;
for(int i=0;i<m;++i)
cin >> f[i];
memset(hs,-1,sizeof(hs)); //将所有集合都初始化为-1,表示未被处理
int n;
cin >> n;
int res=0;
for(int i=0;i<n;++i)
{
int val;
cin >> val;
res^=SG(val);
}
if(res) //如果异或和不为0
puts("Yes");
else
puts("No");
return 0;
}