[AGC009B Tournament] [建模+贪心]

[题目大意]

    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;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值