vijos1476 旅游规划-动态规划

传送门

题解:

我是这么做的,首先第一遍求出每个点向下的不相交的最长链和次长链,

这样两条链拼起来就是就是过这个点(不包含他的父亲的情况下)的最长链。

在这些最长链中取max就可以得到直径。

然后一个点在直径上,要么他本身就在已经求出的最长链上(就是从这个点出发的最长+次长链=直径长度),

要么如果这个点在某一条直径的某条链上(即这个点向父亲方向延伸的最长+这个点向下的最长=直径长度)

事实上还是要在已经求出的最长、次长和向父链上找两条最长的拼起来看是不是直径。

只不过最长肯定不会比次长短所以向父+次长就不用试了。

关于向父链的求法,假设x的某个儿子y,x的向父链为px,则

若y在x的最长链上,那么py=max(px,x的次长链)+1

否则y不在x的最长链上,py=max(px,x的最长链)+1.

最后用ans记录答案排个序即可。注意下标是从0~(n-1)不要写错。

//vijos 1476
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<vector>
#define MAXN 200010
using namespace std;
vector<int> g[MAXN];int ans[MAXN],cnt;
int len1[MAXN],len2[MAXN],maxlen;
int firdfs(int rt,int fa)
{
	len1[rt]=len2[rt]=0;
	for(int i=g[rt].size()-1;i>=0;i--)
		if(g[rt][i]!=fa)
		{
			firdfs(g[rt][i],rt);
			len2[rt]=max(len2[rt],len1[g[rt][i]]+1);
			if(len2[rt]>len1[rt]) swap(len2[rt],len1[rt]);
		}
	return 0;
}
int getdp(int rt,int fa,int flen)
{
//	printf("%d %d\n",rt,flen);
	if(len1[rt]+len2[rt]==maxlen||len1[rt]+flen==maxlen)
		ans[++cnt]=rt;
	for(int i=g[rt].size()-1;i>=0;i--)
		if(g[rt][i]!=fa)
		{
			if(len1[rt]==len1[g[rt][i]]+1)
				getdp(g[rt][i],rt,max(len2[rt],flen)+1);
			else getdp(g[rt][i],rt,max(len1[rt],flen)+1);
		}
}
int main()
{
	int n;scanf("%d",&n);
	for(int i=1;i<n;i++)
	{
		int u,v;scanf("%d%d",&u,&v);
		g[u+1].push_back(v+1);
		g[v+1].push_back(u+1);
	}
	firdfs(5,0);
	for(int i=1;i<=n;i++)
		maxlen=max(maxlen,len1[i]+len2[i]);
	getdp(5,0,0);
/*	printf("%d\n",maxlen);
	for(int i=1;i<=n;i++)
		printf("%d %d\n",len1[i],len2[i]);*/
	sort(ans+1,ans+cnt+1);
	for(int i=1;i<=cnt;i++)
		printf("%d\n",ans[i]-1);
	return 0;
}


  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值