HDU 1729 Stone Game
https://ac.nowcoder.com/acm/contest/34655/C
有 \(n\) 个箱子,第\(i\)个箱子最多放 \(s_i\)个石子,当前箱子里的石子数为 \(c_i\)。两个人轮流往箱子里放石子,而且每一次放的数量都有限制:不能超过当前箱子内石子数的平方。例如箱子里有 \(3\) 颗石子,那么下一个人就可以放\(1-9\) 颗石子,直到箱子被装满。当有一方放不下石子时游戏结束,最后放不下石子的人输。问先手是否能获胜。
常规思路肯定是枚举,然后求出SG函数在异或。
#include<bits/stdc++.h>
using namespace std;
using i32 = int32_t;
using i64 = long long;
using vi = vector<int>;
int mex(vi a) {
ranges::sort(a);
auto [ret, lst] = ranges::unique(a);
a.erase(ret, lst);
for (int i = 0; i < a.size(); i++)
if (a[i] != i) return i;
return a.size();
}
vi g;
int s;
int sg(int x) {
if (g[x] != -1) return g[x];
vi a;
for (int i = 1, X; i <= x * x and i + x <= s; i++)
a.push_back(sg(x + i));
return g[x] = mex(a);
}
i32 main() {
ios::sync_with_stdio(false), cin.tie(nullptr);
int n, res = 0;
cin >> n;
for (int i = 1, c; i <= n; i++) {
cin >> s >> c;
g = vi(s + 1, -1);
res ^= sg(c);
}
if (res != 0) cout << "Yes\n";
else cout << "No\n";
return 0;
}
但是这个思路会T,我们考虑些优化。
首先对于当前状态\((s,c)\)。我们求出最大的\(p\)满足\(p + p ^ 2 < s\),这样的话\((p + 1) + (p + 1) ^ 2 \ge s\)。然后我们考虑\(p\)和\(c\)的关系。
如果\(c > p\),此时先手可以一步放满,因此必胜。
如果\(c = p\),此时先手无法一步放满,后手则可以一步放满,因为先手至少放\(1\),则后手至少可以到达。
如果\(c < p\),状态未知,但是当前状态已知的是谁到\(p\)谁就会输,所以可以递归的查找\((p,c)\)的 SG 函数
而对于\(c>p\)的情况,我们就要打表找规律,找到\((s,c)\)的 SG 函数,这里的结论是\(s - c\)
#include<bits/stdc++.h>
using namespace std;
using i32 = int32_t;
using i64 = long long;
using vi = vector<int>;
int sg(int s, int c) {
assert(s >= c);
int p = sqrt(s);
while(p + p * p >= s) p --;
if (c > p) return s - c;
if (c == p) return 0;
return sg(p, c);
}
i32 main() {
ios::sync_with_stdio(false), cin.tie(nullptr);
int n, res = 0;
cin >> n;
for (int i = 1, s, c; i <= n; i++) {
cin >> s >> c;
res ^= sg(s, c);
}
if (res != 0) cout << "Yes\n";
else cout << "No\n";
return 0;
}