注意到如果把每一對勝者敗者連邊,可以得到一顆樹:
(例子)
但是因為勝者每次只能和一個敗者打,所以要用類似多叉轉二叉的方法,讓有不止一個孩子的節點變成有一個孩子和一個虛點。
如圖,原來 \(1\) 有三個兒子 \(2,3,4\),透過轉換,變成了上圖。
上圖可以直接變成對戰圖(\(x2\to x1\to 1)\):
(原樹不可以換根,否則構造出的對戰圖是錯的!)
對戰圖深度即為樹的深度。
所以這變成了求出轉化後樹深度最小值。
根據貪心,一定是越深的子樹越靠近父節點(經過的虛點最少)。
所以應該是:
於是設 \(f_i\) 為以 \(i\) 為根的子樹深度最小值。
根據上面結論,直接 dp 即可。
也就是求出兒子排列 \(p\) 使得 \(\max_{i=1}^{sz(sons)}f_{p_i}+i\) 最小。
顯然 \(p\) 按照 \(f\) 為關鍵字排序就可以了。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e5 + 5;
vector<int> e[N];
int n, f[N];
void dfs1(int x, int fa)
{
vector<int> v;
for(int i : e[x])
{
dfs1(i, x);
v.push_back(f[i]);
}
sort(v.begin(), v.end(), greater<>());
for(int i = 0; i < v.size(); i ++)
f[x] = max(f[x], v[i] + i + 1);
}
signed main()
{
ios::sync_with_stdio(0);cin.tie(0);
cin >> n;
for(int i = 2; i <= n; i ++)
{
int x; cin >> x;
e[x].push_back(i);
}
dfs1(1, 0);
cout << f[1];
return 0;
}