[题目大意]
n个人参加锦标赛,比赛有若干场,每场都是两个幸存者比试,淘汰失败方。现在不清楚具体怎么比的,只知道1号为最终冠军,其余每个人,i号是被a[i]打败的,让你在所有可能的比赛流程树中找出深度最小的那个,输出深度。
[思路]
考虑建一个新树,其中i的父亲是a[i],这个时候我们发现,i的子树中只有i幸存下来,接下来的比赛和i的后代没有任何关系,即没有后效性,所以我们显然可以贪心地记录一个g[i],表示i的子树全部比完后,形成的以i为根的竞赛树的深度dep[i]的最小值,接着向上合并。
讨论一个点有多个儿子的情况。观察发现,这个点x会依次和每个儿子j节点比一场,每一次比赛相当于令dep[x]=max(dep[x],g[j])+1,我们只需要找到一个最优合并顺序,使得dep[x]尽量小就可以了。不难证明最优策略是按g[j]从小到大的顺序依次合并。
复杂度O(nlogn)。
#include <cstdio>
#include <algorithm>
#define rep(i,j,k) for (i=j;i<=k;i++)
using namespace std;
const int N=1e5+5;
int n,i,l,r,x,y,f,bn,son[N],fs[N],bro[N],fa[N],q[N],b[N],g[N];
void add(int x,int y) {
bro[y]=fs[x]; fs[x]=y;
}
int main()
{
//freopen("tournament.in","r",stdin);
//freopen("tournament.out","w",stdout);
scanf("%d",&n);
rep(i,2,n) scanf("%d",&fa[i]),son[fa[i]]++,add(fa[i],i);
rep(i,1,n) if (!son[i]) q[++r]=i;
for (l=1;l<=r;l++)
{
x=q[l];
if (fs[x]) {
for (y=fs[x],bn=0;y;y=bro[y]) b[++bn]=g[y];
sort(b+1,b+1+bn);
rep(i,1,bn) g[x]=max(g[x],b[i])+1;
}
if (x==1) break;
f=fa[x]; son[f]--; if (!son[f]) q[++r]=f;
}
printf("%d\n",g[1]);
return 0;
}