[AGC064] B - Red and Blue Spanning Tree

[Q] https://atcoder.jp/contests/agc064/tasks/agc064_b

解法が見えなかったので、とりあえず確実にありうることをポンポン実装していく150分だった。結果75/77ACまでしたけどダメだった。

考察
1. 条件が2つ。色条件を満たしていることと、連結であること。
色条件がかなり厳しそう。
連結かどうかは、UnionFindで適当にuniteしていけばいいから後回し。

2. N頂点に対してN-1辺しかない。
1辺で1頂点の色を満たすように張るのでは、N-1頂点の色しか満たせない。1つ足りない。効率よく色を網羅したい。
1辺で2頂点の色を網羅するような辺を、少なくとも1つ設置する必要がある。
S[a] == c && S[b] == cとなる辺a-bを、まず張る。
これは1辺だけでなく、存在する限りいくらでも設置していい。

3.  どうやら、2.の頂点をスタート地点にして、枝を伸ばすように森を構築していく必要があるようだ。
伸ばす先の頂点は色条件を満たすように張る必要がある。
aをスタート地点として、S[b] == cとなるような辺a-bだけを伸ばす。
森がたくさんできるはず。

4. 最後に木にする。M辺全てについて、辺を張ったら連結成分が増えるかどうか確認。増えるなら設置すればいい。
見つけた順に適当でいい。

Q. 枝を伸ばす感じではなく、とにかく色を満たす辺をぽつぽつ設置するのは?
A. ダメ。cycleという名前の2ケースに永遠に落ち続ける。なんかダメなんだろう。

実装

int main() {
  cincout();
  ll N, M;
  cin >> N >> M;
  // G[i][0] = Rの次の辺 G[i][1] = Bの次の辺
  vector<vector<vector<ll>>> G(N, vector<vector<ll>>(2));
  map<tll, ll> memo;
  rep(i, M) {
    ll a, b;
    char c;
    cin >> a >> b >> c;
    --a, --b;
    G[a][c == 'B'].emplace_back(b);
    G[b][c == 'B'].emplace_back(a);
    memo[{a, b, c == 'B'}] = i;
    memo[{b, a, c == 'B'}] = i;
  }
  string S;
  cin >> S;
  vector<ll> SS(N);
  rep(i, N) SS[i] = S[i] == 'B';
 
  UnionFind tree(N);
  queue<ll> Q;
  vector<ll> ans;
  bool ok[N]{};

  // 1辺で2頂点塗れるいいところを探す
  for(auto [t, v]: memo){
    auto [a, b, c] = t;
    // この辺を採用すると、2頂点の色条件をクリアできる。
    if (ok[a] || ok[b]) continue;
    if (SS[a] == c && SS[b] == c){
      tree.unite(a, b);
      ok[a] = true;
      ok[b] = true;
      Q.push(a); // 枝を伸ばすスタート地点を押さえておく。
      Q.push(b);
      ans.emplace_back(v);
    }
  }

  // 森を構築
  while(!Q.empty()){
    ll v = Q.front(); Q.pop();
    rep(j, 2){
      for(auto u: G[v][j]){
        if (ok[u]) continue;
        // 伸ばした先の色条件をクリアしているかどうか。
        if (SS[u] != j) continue;
        ok[u] = true;
        tree.unite(v, u);
        ans.emplace_back(memo[{v, u, j}]);
        Q.push(u);
      }
    }
  }

  // 適当に連結にする
  for(auto [t, v]: memo){
    auto [a, b, c] = t;
    if (tree.issame(a, b)) continue;
    tree.unite(a, b);
    ans.emplace_back(v);
  }
  if (tree.getSize(0) != N){
    cout << "No" << endl;
    return 0;
  }
  rep(i, N){
    if (!ok[i]){
      cout << "No" << endl;
      return 0;
    }
  }
  cout << "Yes" << endl;
  sort(all(ans));
  rep(i, N - 1) cout << (ans[i] + 1) << " \n"[i == N - 2];
}

https://atcoder.jp/contests/agc064/submissions/44571572

適当に構築してよいものと、いけないものとの判別ができなかった。

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