AGC009B 題解

adam01發表於2024-08-03

注意到如果把每一對勝者敗者連邊,可以得到一顆樹:

AGC009B 題解

(例子)

但是因為勝者每次只能和一個敗者打,所以要用類似多叉轉二叉的方法,讓有不止一個孩子的節點變成有一個孩子和一個虛點。

AGC009B 題解

如圖,原來 \(1\) 有三個兒子 \(2,3,4\),透過轉換,變成了上圖。

上圖可以直接變成對戰圖(\(x2\to x1\to 1)\)

(原樹不可以換根,否則構造出的對戰圖是錯的!)

AGC009B 題解

對戰圖深度即為樹的深度。

所以這變成了求出轉化後樹深度最小值。

根據貪心,一定是越深的子樹越靠近父節點(經過的虛點最少)。

所以應該是:

AGC009B 題解

於是設 \(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;
}