Atcoder AGC #009 B. Tournament
Atcoder AGC #009 B. Tournament
B: Tournament - AtCoder Grand Contest 009 | AtCoder
解いたあとに解説見たらDPでごにょごにょって書いていたが、もっとわかりやすくあっさり書けた。
n人の人が、負けたらそこで終わりのトーナメント形式で優勝を目指して戦う。番号1の人が優勝したことはわかっており、他の人が誰に負けたかという情報も与えられる。ありうるトーナメントの形のうち、深さの最小値を求める問題。まず番号1を根として、AがBに勝ったという情報から、AからBに子をつくるように木を構成してみる。そこからトーナメントの形を構成しようと考えると、相異なる親をもつ葉を同時にコスト1で削除していく操作を繰り返すことで、根以外のノードをすべて削除するまでにかかるコスト、が求める最小値に一致することが証明できる。あるノードを親としてその複数の子ノードについて考える。それら以下のノード全てを削除するためのコストはわかっているものとしてよい。それが例えば、1, 1, 1だったとすると、コストは親を削除するコストを除いて3になる。なぜなら、削除の操作は親ノードが一致する場合は同時に行えないので、1つずつ削除するしかないからである。同様に、コストが1, 2, 3である場合も3になることがわかる。なぜなら、1回目の操作でコストは0, 1, 2になり、2回目の操作で0, 0, 1になり、3回目の操作で0, 0, 0になるからである。結局、あるノードのコストがxであることを、そのノードはx秒後以降に削除できる、という条件に読み替えて、全てのノードを削除するのに何秒かかるかという問題に帰着できる。これは昇順にソートして、貪欲に調べて行くことができる。計算量はソートの部分が一番大きく、O(nlogn)。
#include "bits/stdc++.h" using namespace std; #define rep(i, n) for (int (i) = 0; (i) < (int)(n); (i)++) #define all(x) (x).begin(), (x).end() vector<int> g[101010]; int dfs(int s) { int res = 0; vector<int> v; for (auto ns : g[s]) v.push_back(dfs(ns)); sort(all(v)); //削除コスト順にソート rep(i, v.size()) res = max(res + 1, v[i]); //小さいものから見ていき、今見ているノードか、時刻+1の大きい方をとる return res + 1; //親ノードを削除するコストを足す } signed main() { int n; cin >> n; rep(i, n - 1) { int a; cin >> a; a --; g[a].push_back(i + 1); } cout << dfs(0) - 1 << endl; return 0; }
AGCのB問題はすべて埋めたので、次はC問題を埋めていく。