競技プログラミング日記

主に AtCoder の記事です

AtCoder Beginner Contest 314F問題

\(\def \set #1#2{\{ #1 \ \vert \ #2 \}}\)

ABC314F

問題概要

有向 tree \(T\) を構成する.

  • \(N := \{0, 1, \cdots, N-1\}\)
  • \(|V(T)| = 2N-1\)
  • The set \(L := \set{\{i\}}{ i \in N}\) is the set of leafs of T.
  • For any vertex \(x\) of \(V\) is a subset of \(L\).
  • There is a unique \(r \in V(T)\) such that \(r = N\). We denote that \(r\) is a root of \(T\).
  • For any distinct vertices \(x\) and \(y\) with same parents such that \(x \cap y\) is the empty set.
解法

プレイヤー \(i \in N\) が,どの vertex \(in V(T)\) に属すのかを \(gp : N \rightarrow V(T)\) で表す.

\(V(T)\) に対して,代表元を決めながら合併する. \(p, q \in N\) の試合の後に新しくできる vertex を \(v \in V(T)\) とおく.
試合の後にするべき操作は,

  • 頂点の更新:
    • \(gp(p)\) と \(gp(q)\) のマージ.
    • 代表元の更新: \(gp(leader(v)) = v\)
  • 辺の更新:
    • \(v \rightarrow gp(p)\), \(v \rightarrow gp(q)\) を作る.

するべき操作が決まれば,用意すべきものが決まる.
用意するものは,

  • 各 \(w in V(T)\) に対する代表元
  • 各 \(i \in N\) に対して,今 \(i\) が属している \(V(T)\) の元であって, 一番新しく作成されたもの.

これらは,Uinon-Find で実現できる.

使っている記号,マクロ等 "https://ecsmtlir.hatenablog.com/entry/2022/12/23/131925"

int main() {
  ll n;
  cin >> n;

  UnionFind<ll> uf(n);
  vll gp(n); rep(i,n) gp[i] = i;
  vll sz(2*n-1,1);

  vvll to(2*n-1);
  rep(i,n-1){
    ll p,q; cin >> p >> q;
    --p, --q;
    p = uf[p];
    q = uf[q];

    // new vertex v
    ll v = i + n;
   
    // new edges
    // v -> gp[p],
    // v -> gp[q]
    to[v] = vll({gp[p], gp[q]});

    // vertex
    sz[v] = uf.size(p) + uf.size(q); // rem : not sz[uf.size(p)]
    uf.merge(p,q);
    gp[p] = v; // p = leader of v
  }

  ll root = 2*n-1 -1;
  vector<mint> dp(2*n-1);
  auto dfs = [&](auto dfs, ll cu) -> void {
    for(ll ne : to[cu]) {
      dp[ne] = dp[cu] + mint(sz[ne])/mint(sz[cu]);
      dfs(dfs, ne);
    }
  };
 
  dfs(dfs, root);
  rep(i,n) cout << dp[i].val() << " ";

  return 0;
}