容斥原理、博弈论
- 容斥原理
- 890. 能被整除的数(二进制状态压缩版本,复杂度多一个Om)
- 890. 能被整除的数(dfs版本)
- 博弈论
- 无限制nim游戏
- AcWing 891. Nim游戏
- AcWing 892. 台阶-Nim游戏(待补)
- 集合版本Nim游戏
- AcWing 893. 集合-Nim游戏
- AcWing 894. 拆分-Nim游戏(待补)
容斥原理
容斥原理可以画一个韦恩图来看各个集合的关系
890. 能被整除的数(二进制状态压缩版本,复杂度多一个Om)
#include <iostream>
#include <algorithm>
#include <cmath>using namespace std;
typedef long long LL;
const int N = 17;
int p[N];
void solve()
{int n, m;cin >> n >> m;for (int i = 0; i < m; ++ i) cin >> p[i];int res = 0;for (int i = 1; i < 1 << m; ++ i)//枚举2的n次方-1个集合{int t = 1, cnt = 0;for (int j = 0; j < m; ++ j)//判断是否乘上p[j]{if (i >> j & 1){if ((LL)t * p[j] > n){t = -1;break;}t *= p[j];cnt ++ ;}}if (t != -1){if (cnt & 1) res += n / t;//奇数就加上,偶数就减去else res -= n / t;}}cout << res;
}
int32_t main()
{ios::sync_with_stdio(0);cin.tie(0);int T = 1;//cin >> T;while (T --) solve();return 0;
}
890. 能被整除的数(dfs版本)
本题本质上就是一个枚举所有答案的过程,那么我们当然可以用dfs搜索到所有可能的方案
#include <iostream>
#include <algorithm>
#include <cmath>using namespace std;
typedef long long LL;
const int N = 17;
int p[N];
int n, m;
int res = 0;
void dfs(int u, int t, bool add)
{if (u == m){if (t == 1) return ;else {if (add) res += n / t;else res -= n / t;return;} }dfs(u + 1, t, add);//m个质数中不选择p[i]if ((LL)t * p[u] <= n)//m个质数中不选择p[i]{dfs(u + 1, t * p[u], !add);//本层选了一个数的话,下一层枚举的集合的add就要取反,因为容斥原理公式就是1个的+,2个的-,3个的+,每多选一个数字,加减号相反}
}
void solve()
{cin >> n >> m;for (int i = 0; i < m; ++ i) cin >> p[i];dfs(0, 1, false);cout << res;
}
int32_t main()
{ios::sync_with_stdio(0);cin.tie(0);int T = 1;//cin >> T;while (T --) solve();return 0;
}
博弈论
- 为什么异或值=0先手必败?这个我没搞懂,我主要是记住这个结论
- 为什么异或值!=0先手必胜?
因为它可以转化为对手先手必败的状态(异或值为0),推导如下
无限制nim游戏
AcWing 891. Nim游戏
#include <iostream>
#include <algorithm>
#include <cmath>using namespace std;
const int N = 1e5 + 10;
int a[N];
void solve()
{int n;cin >> n;for (int i = 0; i < n; ++ i) cin >> a[i];int t = 0;//异或运算里面的0相当于加法里面的0,乘法里面的1for (int i = 0; i < n; ++ i) {t ^= a[i];}if (t) cout << "Yes" << endl;else cout << "No" << endl;
}
int32_t main()
{ios::sync_with_stdio(0);cin.tie(0);int T = 1;//cin >> T;while (T --) solve();return 0;
}
AcWing 892. 台阶-Nim游戏(待补)
集合版本Nim游戏
AcWing 893. 集合-Nim游戏
- 集合版本Nim游戏的每一步有多种选择,但多种选择是被限制在一个选择集合中的(而不是随意拿多少个)。
- sg(起点) != 0说明 我经过若干步一定可以到达 sg = 0的点,即我还是可以操作的,如果sg(起点) = 0,那我没有任何操作空间,直接判负,即先手必败。
- 推荐看这个博客
#include <iostream>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <set>
using namespace std;
const int N = 1e4 + 10;
int s[N], f[N];//f可以不用,但可以起到剪枝的效果
int n, k;int sg(int x)
{if (f[x] != -1) return f[x];set<int> S;for (int i = 0; i < k; ++ i)if(x >= s[i]) S.insert(sg(x - s[i]));//这里如果不判断x>=s[i]的话会影响后续路线的赋值,//本来下一层应该是1,2但是因为负数,变成了0, 1//那么本层本来是0的,递归回来的时候现在也会被影响变成了2for (int i = 0; i < k; ++ i){if (S.count(i) == 0) return f[x] = i;}
}void solve()
{memset(f, -1, sizeof f);cin >> k;for (int i = 0; i < k; ++ i) cin >> s[i];cin >> n;int x;int res = 0;while(n --){cin >> x;res ^= sg(x);}if (res) cout << "Yes" << endl;else cout << "No" << endl; }
int32_t main()
{ios::sync_with_stdio(0);cin.tie(0);int T = 1;//cin >> T;while (T --) solve();return 0;
}
AcWing 894. 拆分-Nim游戏(待补)