#include<bits/stdc++.h> #define watch(x) std::cout << (#x) << " is " << (x) << std::endl using LL = longlong;
intmain(){ //freopen("in","r",stdin); std::ios::sync_with_stdio(false); std::cin.tie(nullptr); std::cout << "请输入一个正数组,以 0 结尾,输出下一步的必胜策略:" << std::endl; std::vector<int> a; int n; while (std::cin >> n && n) a.emplace_back(n); int s = 0; for (auto x : a) s ^= x; if (s == 0) std::cout << "必败,随便选择一个合理策略吧,等待对手失误吧" << std::endl; else { for (auto &x : a) if (x ^ s <= x) { x ^= s; break; } std::cout << "以下是下一步的一个必胜策略:\n"; for (auto x : a) std::cout << x << " "; std::cout << std::endl; } return0; }
#include<bits/stdc++.h> #define watch(x) std::cout << (#x) << " is " << (x) << std::endl using LL = longlong;
intSG(){ // n 个节点,0 为起点,m 条有向边,确保无环。 int n, m; std::cin >> n >> m; std::vector<std::vector<int>> e(n); for (int i = 0; i < m; ++i) { int x, y; std::cin >> x >> y; e[x].emplace_back(y); } std::vector<int> sg(n, -1); std::function<int(int)> dfs = [&](int u) -> int { if (sg[u] != -1) return sg[u]; std::set<int> S; for (auto v : e[u]) S.insert(dfs(v)); sg[u] = 0; while (S.find(sg[u]) != S.end()) ++sg[u]; return sg[u]; }; return dfs(0); }
intmain(){ //freopen("in","r",stdin); std::ios::sync_with_stdio(false); std::cin.tie(nullptr); int nroot; std::cin >> nroot; int ans = 0; while (nroot--) { ans ^= SG(); } std::cout << (ans ? "先手赢" : "后手赢") << std::endl; return0; }
可以做到二维平面上有障碍点集,又可以做一个题目了。
注意到上述问题,堆与堆之间是相互独立的,如果不独立,那就很难考虑了。
阶梯 Nim
有 m 堆石子 $(a_1, \cdots, a_m)$ 每次可以从 i 位置移动 $1 \leq c_i \leq a_i$ 个石子到 i - 1 的位置。两人轮流进行,无法移动者输。
#include<bits/stdc++.h> #define watch(x) std::cout << (#x) << " is " << (x) << std::endl using LL = longlong;
boolwin(conststd::vector<int> &a, int p, int q){ int sg = 0, n = a.size(); if (p > q) { for (int i = 0; i < n; ++i) if (a[i] > q) return1; for (auto x : a) sg ^= x; } elseif (p == q) { for (auto x : a) sg ^= x % (1 + p); } else { int ai = -1; for (auto x : a) { sg ^= x; if (x > p) { if (ai != -1) return0; ai = x; } } if (ai != -1) { int aii = sg ^ ai; return aii <= p && aii <= ai && ai - aii <= p; } } return sg; }
intmain(){ //freopen("in","r",stdin); std::ios::sync_with_stdio(false); std::cin.tie(nullptr); int n, p, q; std::cin >> n >> p >> q; std::vector<int> a(n); for (auto &x : a) std::cin >> x; std::cout << (win(a, p, q) ? "first player win" : "second player win") << std::endl; return0; }
boolsolve(LL n){ while (n & 1) n >>= 1; while (n) { n >>= __builtin_ctzll(n); bool flag = false; while (n & 1) { n >>= 1; flag = !flag; } if (flag) returntrue; } returnfalse; }
classNim { std::vector<int> sg{0}; public: intf(int n, int x){ int r = n; for (int i = 1; i < n; i *= 2) if ((n & i) == 0) { if (x & 1) r -= i; x /= 2; if (x == 0) break; } if (x > 0) return-1; return r; } voidinit(int n){ for (int i = sg.size(); i < n; ++i) { if (i & 1) { sg.emplace_back(sg[i >> 1]); continue; } std::set<int> S; for (int j = 1; j < i; ++j) { int now = f(i, j); if (now < 0) break; S.insert(sg[now]); } int r = 0; while (S.count(r)) ++r; sg.emplace_back(r); } } intsolve(conststd::vector<int> &a){ init(*std::max_element(a.begin(), a.end()) + 1); int r = 0; for (auto x : a) r ^= sg[x]; return r; } };
classNim { std::vector<int> sg{0}; public: intf(int n, int x){ int r = n; for (int i = 1; i < n; i *= 2) if ((n & i) == 0) { if (x & 1) r -= i; x /= 2; if (x == 0) break; } if (x > 0) return-1; return r; } voidinit(int n){ if (sg.size() >= n) return; std::vector<int> cnt(n + 2); for (int i = sg.size(); i < n; ++i) { if (i & 1) { sg.emplace_back(sg[i >> 1]); continue; } std::stack<int> S; for (int j = 1; j < i; ++j) { int now = f(i, j); if (now < 0) break; ++cnt[sg[now]]; S.push(sg[now]); } int r = 0; while (cnt[r]) ++r; sg.emplace_back(r); while (!S.empty()) { --cnt[S.top()]; S.pop(); } } } intsolve(conststd::vector<int> &a){ init(*std::max_element(a.begin(), a.end()) + 1); int r = 0; for (auto x : a) r ^= sg[x]; return r; } };
做法:考虑二进制,对于任意给定位,这一位的 1 的个数和都是 m 的倍数,那么就后手赢,否则先手赢。
对于否则的情况,我们总可以找到考虑最高位不是 m 的倍数,然后随便选择余数个(当前位为 1 的)进行改变。并且这些数后面的位是可以任意改变的。然后再继续往低位跑,显然,最终选取的个数总和小于 m。
1 2 3 4 5 6 7 8 9 10 11
// 每次选择在 x 堆(0 < x < m) 取石子,不能取的输 boolmultNim(conststd::vector<LL> &a, int m){ LL mx = *std::max_element(a.begin(), a.end()); int ln = std::__lg(mx + 1) + 1; for (int i = 0; i < ln; ++i) { int cnt = 0; for (auto x : a) if ((x >> i) & 1) ++cnt; if (cnt % m) returntrue; } returnfalse; }
// 不公平博弈,a[i] 值域为 {1, -1} doublesurrealNumber(std::vector<int> a){ double r = 0; int i = 0, n = a.size(); while (i < n && a[i] == a[0]) r += a[i], ++i; for (double k = 2; i < n; ++i, k *= 2) r += a[i] / k; return r; } // 模板例题:https://vjudge.net/problem/POJ-2931