1.异或的性质
🏳️🌈🏳️🌈🏳️🌈🏳️🌈🏳️🌈🏳️🌈
2.nim游戏 (基础)
891. Nim游戏 - AcWing题库
给定 n 堆石子,两位玩家轮流操作,每次操作可以从任意一堆石子中拿走任意数量的石子(可以拿完,但不能不拿),最后无法进行操作的人视为失败。
问如果两人都采用最优策略,先手是否必胜。
输入格式
第一行包含整数 n。
第二行包含 n 个数字,其中第 i 个数字表示第 i 堆石子的数量。
输出格式
如果先手方必胜,则输出
Yes
。否则,输出
No
。
例如:有两堆石子,第一堆有2个,第二堆有3个,先手必胜。
操作步骤:
1. 先手从第二堆拿走1个,此时第一堆和第二堆数目相同
2. 无论后手怎么拿,先手都在另外一堆石子中取走相同数量的石子即可。
先手必败状态:a1 ^ a2 ^ a3 ^ ... ^an = 0
先手必胜状态:a1 ^ a2 ^ a3 ^ ... ^an ≠ 0
#include <iostream>
#include <cstdio>
using namespace std;
/*
先手必胜状态:先手操作完,可以走到某一个必败状态
先手必败状态:先手操作完,走不到任何一个必败状态
先手必败状态:a1 ^ a2 ^ a3 ^ ... ^an = 0
先手必胜状态:a1 ^ a2 ^ a3 ^ ... ^an ≠ 0
*/
int main(){
int n;
scanf("%d", &n);
int res = 0;
for(int i = 0; i < n; i++) {
int x;
scanf("%d", &x);
res ^= x;
}
if(res == 0) puts("No");
else puts("Yes");
}
3.nim游戏(变形)
892. 台阶-Nim游戏 - AcWing题库
现在,有一个 n 级台阶的楼梯,每级台阶上都有若干个石子,其中第 i 级台阶上有 ai 个石子(i≥1)。
两位玩家轮流操作,每次操作可以从任意一级台阶上拿若干个石子放到下一级台阶中(不能不拿)。
已经拿到地面上的石子不能再拿,最后无法进行操作的人视为失败。
问如果两人都采用最优策略,先手是否必胜。
输入格式
第一行包含整数 n。
第二行包含 n 个整数,其中第 i 个整数表示第 i 级台阶上的石子数 ai
输出格式
如果先手方必胜,则输出
Yes
。否则,输出
No
。
此时我们需要将奇数台阶看做一个经典的Nim游戏,如果先手时奇数台阶上的值的异或值为0,则先手必败,反之必胜
证明:
先手时,如果奇数台阶异或非0,根据经典Nim游戏,先手总有一种方式使奇数台阶异或为0,于是先手留了奇数台阶异或为0的状态给后手
于是轮到后手:
①当后手移动偶数台阶上的石子时,先手只需将对手移动的石子继续移到下一个台阶,这样奇数台阶的石子相当于没变,于是留给后手的又是奇数台阶异或为0的状态
②当后手移动奇数台阶上的石子时,留给先手的奇数台阶异或非0,根据经典Nim游戏,先手总能找出一种方案使奇数台阶异或为0
只异或奇数的,不异或偶数的,因为最后要把石子都放到地面,地面是第0层(偶数层,如果算上地面的,也许会多一层,个人理解)
#include <iostream>
using namespace std;
int main()
{
int res = 0;
int n;
cin >> n;
for(int i = 1 ; i <= n ; i++)
{
int x;
cin >> x;
if(i % 2==1) res ^= x;//只异或奇数的
}
if(res==0) puts("No");
else puts("Yes");
return 0;
}
⭐⭐⭐代码中,先都异或了一遍,然后把
修改的位置的原来的数和修改了的数又异或了一遍
刚开始以为这不是重复了吗
其实不是,以为相同的数异或为0,0异或任何数还为原数
那么 修改的位置的原来的数异或后是0,并不影响
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N = 1e5+5;
int a[N];
signed main(){
int n,q,x,y;
cin>>n>>q;
int sum = 0;
for(int i=1;i<=n;i++){
cin>>a[i];
sum^=a[i];
}
while(q--){
cin>>x>>y;
sum^=a[x];
sum^=y;
a[x] = y;
if(sum!=0){
cout<<"Kan"<<endl;
}
else{
cout<<"Li"<<endl;
}
}
return 0;
}
Code over!