HDU 1729 Stone Game

PHarr發表於2024-09-05

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;
}