纪中暑假集训 2020.08.03【NOIP提高组】模拟 T4:【SDOI2013】直径

【SDOI2013】直径

Description

小 Q 最近学习了一些图论知识。根据课本,有如下定义。
树:无回路且连通的无向图,每条边都有正整数的权值来表示其长度。如果一棵树有 N 个节点,可以证明其有且仅有 N-1 条边。
路径:一棵树上,任意两个节点之间最多有一条简单路径。我们用 dis(a,b)表示点 a 和点 b 的路径上各边长度之和。称 dis(a,b)为 a、b 两个节点间的距离。
直径:一棵树上,最长的路径为树的直径。树的直径可能不是唯一的。
现在小 Q 想知道,对于给定的一棵树,其直径的长度是多少,以及有多少条边满足所有的直径都经过该边

Input

第一行包含一个整数 N,表示节点数。
接下来 N-1 行,每行三个整数 a, b, c,表示点 a 和点 b 之间有一条长度为 c的无向边。

Output

共两行。第一行一个整数,表示直径的长度。第二行一个整数,表示被所有直径经过的边的数量。

Sample Input

6
3 1 1000
1 4 10
4 2 100
4 5 50
4 6 100

Sample Output

1110
2

Data Constraint

对于 20%的测试数据:N≤100
对于 40%的测试数据:N≤1000
对于 70%的测试数据:N≤100000
对于 100%的测试数据:2≤N≤200000,所有点的编号都在 1…N 的范围内,边的权值≤10^9。
对于每个测试点,若输出文件的第一行与标准输出相同,则得到该测试点20%的分数,若输出文件的第二行与标准输出相同,则得到该测试点 80%的分数,两项可累加。
本题使用自定义校验器,为防止自定义校验器出错,即使你无法正确得出某一问的答案,也应在相应的位置随便输出一个数字。

Hint

直径共有两条,3 到 2 的路径和 3 到 6 的路径。这两条直径都经过边(3, 1)和边(1, 4)。

反思&题解

比赛思路: 刚开始想的是点分治,干了1h后放弃了,于是就SPFA骗了8分
正解思路: 首先对于树的直径的性质和求解方法不太理解的可以看看这篇博客
首先,我们先以节点1为根跑一遍dfs,求出离1号节点最远的节点(如果有多个就随便选一个),再以这个节点为根建树跑一遍dfs,求出离这个节点最远的节点,他们之间的距离就是直径(顺便记录一下每个节点的深度)
现在麻烦就在第二问,对于树的直径有一个性质:若一棵树有多条直径,那么这些直径毕相交于一个点,知道了这个性质就可以来做
我们在跑一遍dfs,先求出以每个节点为根的子树中与根节点间长度最长的长度即节点个数,之后分两种情况讨论:
1. 如果这个字数内与根节点距离最长的节点与当前节点之间的长度等于当前节点与根节点之间的长度,那么也就是说着两条边可以互相替换,也就是说当前节点父亲节点及以上的节点都没有用处了,就直接将 a n s = d e p [ n o w ] 1 ans-=dep[now]-1 ,之后直接输出,就不用往上回去了
2. 如果以当前节点为根的子树中有两及以上个与根节点距离最大的点,那么都在两个直径中的答案是当前节点的祖先节点,即 a n s = d e p [ n o w ] 1 ans=dep[now]-1
反思: 还是对于树的题目要多刷,这个大知识点很重要

CODE

#include<bits/stdc++.h>
using namespace std;
struct arr
{
	long long next,to,w;
}edge[400005];
long long n,head[400005],dep[200005],dis[200005],cnt,root,k,fa[200005],ans,bz[200005],dis2[200005];
void add(long long u,long long v,long long len)
{
	edge[++cnt].to=v;
	edge[cnt].w=len;
	edge[cnt].next=head[u];
	head[u]=cnt;	
}
void dfs1(long long now,long long fa)
{
	long long i;
	for (i=head[now];i;i=edge[i].next)
	{
		long long v=edge[i].to;
		if (v!=fa)
		{
			dis[v]=dis[now]+edge[i].w;
			dfs1(v,now);
		}
	}
}
void dfs2(long long now)
{
	long long i;
	for (i=head[now];i;i=edge[i].next)
	{
		long long v=edge[i].to;
		if (v!=fa[now])
		{
			fa[v]=now;
			dep[v]=dep[now]+1;
			dis[v]=dis[now]+edge[i].w;
			dfs2(v);
		}
	}
}
void dbz(long long x)
{
	while (x!=root)
	{
		bz[x]=true;
		x=fa[x];
	}
}
void dfs3(long long now)
{
	long long i,mx=0,tot;
	bool flag=false;
	for (i=head[now];i;i=edge[i].next)
	{
		long long v=edge[i].to;
		if (v!=fa[now])
		{
			dfs3(v);
			if (dis2[v]>=mx)
			{
				if (dis2[v]>mx)
				{
					mx=dis2[v];
					tot=1;
				}
				else tot++;
			}
			if (dis2[v]-dis[now]==dis[now]) flag=true;
		}
	}
	if (bz[now])
	{
		if (flag)
		{
			ans-=dep[now]-1;
			if (ans>0) printf("%lld\n",ans);
			else printf("0\n");		
		}
		if (tot>=2) ans=dep[now]-1;
	}
	mx=max(mx,dis[now]);
	dis2[now]=mx;
}
int main()
{
	scanf("%lld",&n);
	long long i;
	for (i=1;i<n;i++)
	{
		long long a,b,c;
		scanf("%lld%lld%lld",&a,&b,&c);
		add(a,b,c);
		add(b,a,c);
	}
	dfs1(1,0);
	root=1;
	for (i=2;i<=n;i++)
		if (dis[i]>dis[root]) root=i;
	memset(dis,0,sizeof(dis));
	dep[root]=1;
	dfs2(root);
	k=1;
	for (i=2;i<=n;i++)
		if (dis[i]>dis[k]) k=i;
	printf("%lld\n",dis[k]);
	ans=dep[k]-1;
	dbz(k);
	dfs3(root);
	printf("%lld\n",ans);
	return 0;
}

猜你喜欢

转载自blog.csdn.net/CMC_YXY/article/details/107773687