[ARC162] C - Mex Game on Tree

[Q] https://atcoder.jp/contests/arc162/tasks/arc162_c

考察
・BobはKの値を書くのが強い。
AliceがK=2を作りたい場合、部分木は{0, 1}で構成されている必要がある。
Bobが妨害してK=2を置いて{0, 1, 2}としてしまえば、必ずKは3以上にできるので、部分木を殺せる。

・Aliceが勝つには?
こんな部分木が存在すれば勝てそう。
ex, K=2のとき
1. 部分木が{0, 1}で構成されていて、-1がない場合はすでに確定。
2. 部分木が{0}で構成されていて、-1が1個ある場合。-1を1にしてしまえば確定。
3. 部分木が{1}で構成されていて、-1が1個ある場合。-1を0にしてしまえば確定。

・Aliceが満たせないような部分木は?
1. 部分木に{2}が含まれている。
2. -1が2個以上ある場合。後追いで{2}を書かれてしまう。

1つでも勝てる部分木を探せばいい。
また、Nの総数がたったの2000しかない。
難しいことは考えずに、すべての頂点から見た部分木を全部シミュレートすることが可能。

・実装重くなりそうだけど頑張る。

実装

int main() {
  ll T;
  cin >> T;
  rep(_t, T) {
    ll N, K;
    cin >> N >> K;
    vector<ll> G[N]; // 根から葉
    vector<ll> R[N]; // 葉から根
    vector<set<ll>> S(N); // 部分木に含まれている要素を除いた集合
    set<ll> _s;
    rep(i, N + 3) _s.emplace(i);
    rep(i, N) S[i] = _s;
    for (ll i = 1; i < N; ++i) {
      ll p;
      cin >> p;
      --p;
      G[p].emplace_back(i); // 根から葉
      R[i].emplace_back(p); // 葉から根
    }
    vector<ll> A(N);
    vector<ll> F(N, 0); // -1の数
    for (auto &a : A) cin >> a;

    // 毎回根まで戻って、各頂点の部分木の値をメモしていく
    auto back_root = [&](ll s) {
      ll a = A[s];
      while (1) {
        if (a == -1)
          ++F[s];
        else {
          S[s].erase(a);
        }
        if (s == 0) return;
        s = R[s][0];
      }
    };
    std::function<void(ll)> dfs = [&](ll v) {
      back_root(v);
      for (auto w : G[v]) {
        dfs(w);
      }
    };
    dfs(0);
    bool is_bob = true;
    rep(i, N) {
      if (*S[i].begin() == K && F[i] <= 1) { // 部分木の最小値がKで、-1が1個以下なら確定
        is_bob = false;
        break;
      }
      {
        auto it = S[i].begin();
        ++it;
        if (*it == K && F[i] == 1) { // -1が1つで、1つ補完してKになる場合確定。
          is_bob = false;
          break;
        }
      }
    }
    if (is_bob) {
      cout << "Bob" << endl;
    } else {
      cout << "Alice" << endl;
    }
  }
}

https://atcoder.jp/contests/arc162/submissions/42728364

いいなと思ったら応援しよう!