公式

E - Taka and Hashi 解説 by Nyaan


高橋君が移動する様子を考えます。高橋君が頂点 \(a\) の次に頂点 \(b\) に出現した時、

  • 自身が \(ab\) 辺を通過して \(a\) から \(b\) へ移動するか、
  • 頂点 \(a\) で分裂して、タカとハシがともに頂点 \(b\) に移動してそこで合体する

という 2 通りのいずれかであることがわかります。よって補助グラフ \(G_1\)

  • 高橋君が通れる辺の両端、および
  • 以下の条件を満たす頂点の組 \((a,b)\)
    • タカとハシが共に、頂点 \(a\) を出発して合体することなく自身が通れる辺を経由して頂点 \(b\) に到達できる。

の間に辺を貼ったグラフとした時、\(G_1\) 上で頂点 \(1\) と連結な頂点が答えとなります。
よって \(G_1\) と連結性において等価なグラフを構成すれば答えを求められます。これは補助グラフ \(G_2, G_3\) をそれぞれラベル \(2\) の辺、ラベル \(3\) の辺のみからなるグラフとした時、\(G_2, G_3\) に対応する Union-Find \(U_2, U_3\) を構成して (U_2.leader(i), U_3.leader(i)) に注目することで計算できます。
計算量は \(\mathrm{O}((N+M) \alpha(N))\) 程度で十分高速です。

  • 実装例(C++)
#include <iostream>
#include <map>
#include <utility>
#include <vector>
using namespace std;

#include "atcoder/dsu.hpp"

int main() {
  cin.tie(0)->sync_with_stdio(0);

  int T;
  cin >> T;
  while (T--) {
    int N, M;
    cin >> N >> M;
    atcoder::dsu uf1(N), uf2(N), uf3(N);
    for (int i = 0; i < M; i++) {
      int u, v, l;
      cin >> u >> v >> l;
      --u, --v;
      (l == 1 ? uf1 : l == 2 ? uf2 : uf3).merge(u, v);
    }

    map<pair<int, int>, int> mp;
    for (int i = 0; i < N; i++) {
      auto p = make_pair(uf2.leader(i), uf3.leader(i));
      if (mp.count(p)) uf1.merge(mp[p], i);
      mp[p] = i;
    }

    vector<int> ans;
    for (int i = 0; i < N; i++) {
      if (uf1.same(0, i)) ans.push_back(i + 1);
    }
    cout << ans.size() << "\n";
    for (int i = 0; i < (int)ans.size(); i++) {
      cout << ans[i] << " \n"[i + 1 == (int)ans.size()];
    }
  }
}

投稿日時:
最終更新: