CF2006A Iris and Game on the Tree

空白菌發表於2024-09-23

題目連結

題解

知識點:貪心,博弈論。

一個 \(01\) 串中 \(01, 10\) 的個數差只與首尾兩個字元相關,若首尾字元相同,則個數差為 \(0\) ,否則為 \(1\)\(-1\) 。因此,樹上除了根節點和葉子節點的 \(?\) 是不影響葉子節點權值的(但可能影響策略,導致答案不一樣),我們只需要考慮葉子節點和根節點的情況即可。

\(cnt_0, cnt_1, cnt_2\) 分別為值為 \(0, 1, ?\) 的葉子節點個數。

如果根節點不是 \(?\) ,那麼答案顯然為與根節點值相反的葉子節點數,加上 \(\lceil cnt_2 / 2 \rceil\) 。接下來考慮根節點是 \(?\) 的情況。

\(cnt_0 \neq cnt_1\) 時,不妨設 \(cnt_0 < cnt_1\) ,先手有如下幾種策略:

  1. 先手第一步確定根節點的值為 \(0\) ,此時答案為 \(cnt_1 + \lfloor cnt_2 / 2 \rfloor\)
  2. 先手第一步確定根節點的值為 \(1\) ,此時答案為 \(cnt_0 + \lfloor cnt_2 / 2 \rfloor\)
  3. 先手第一步確定一個葉子節點為 \(0\) ,那麼後手可以確定根節點為 \(1\) ,此時答案為 \(cnt_0 + \lfloor cnt_2 / 2 \rfloor + 1\)
  4. 先手第一步確定一個葉子節點為 \(1\) ,那麼後手可以確定根節點為 \(1\) ,此時答案為 \(cnt_0 + \lfloor cnt_2 / 2 \rfloor\)

顯然第一種策略答案最優。

\(cnt_0 = cnt_1\) 時,有如下幾種情況:

  1. 不存在非根節點和葉子節點的 \(?\) ,無論如何答案都為 \(cnt_0 + \lfloor cnt_2 / 2 \rfloor\)
  2. 存在一個非根節點和葉子節點的 \(?\) ,先手可以選擇一個非根節點和葉子節點的 \(?\) 隨意賦值,後手無論如何賦值,答案都為 \(cnt_0 + \lceil cnt_2 / 2 \rceil\)
  3. 存在多個非根節點和葉子節點的 \(?\) ,如果數量為偶數,則與不存在的情況等價,否則與存在一個的情況等價。

時間複雜度 \(O(n)\)

空間複雜度 \(O(n)\)

程式碼

#include <bits/stdc++.h>
using namespace std;
using ll = long long;

int deg[100007];

bool solve() {
    int n;
    cin >> n;
    for (int i = 1;i <= n;i++) deg[i] = 0;
    for (int i = 2;i <= n;i++) {
        int u, v;
        cin >> u >> v;
        deg[u]++;
        deg[v]++;
    }
    string s;
    cin >> s;
    s = "?" + s;
    int cnt[3] = {};
    for (int i = 2;i <= n;i++) if (deg[i] == 1) cnt[s[i] == '?' ? 2 : s[i] == '1']++;
    if (s[1] != '?') cout << cnt[s[1] == '0'] + (cnt[2] + 1) / 2 << '\n';
    else {
        if (cnt[0] == cnt[1]) {
            int delta = count(s.begin() + 1, s.end(), '?') - cnt[2] - 1;
            cout << cnt[0] + (cnt[2] + (delta & 1)) / 2 << '\n';
        }
        else cout << max(cnt[0], cnt[1]) + cnt[2] / 2 << '\n';
    }
    return true;
}

int main() {
    std::ios::sync_with_stdio(0), std::cin.tie(0), std::cout.tie(0);
    int t = 1;
    cin >> t;
    while (t--) {
        if (!solve()) cout << -1 << '\n';
    }
    return 0;
}