BZOJ3124: [Sdoi2013]直径

[文章目录]

Description

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

神题啊,卡死我了。。。原来想当水题切的,越写越乱。更博只是想说两条性质。

1.所有直径都过的边一定是连在一起的。

2.直径们一定是类似左面一个大根,中间一条链,右面一条大根。或是可能没有链,直接一个大菊花的形状。

还有一点,直径求法不只有树形DP。dfs*2也挺好

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long ll;
int n,start,tar,fna;
int path[201000];
int head[201000],to[401000],nxt[401000],data[401000],cnt;
ll dis[201000],ans; 
void add(int x,int y,int z)
{
	to[++cnt]=y;
	nxt[cnt]=head[x];
	head[x]=cnt;
	data[cnt]=z;
}
void dfs(int x,int pre,ll deep)
{
	if(deep>ans) ans=deep,tar=x;
	for(int i=head[x];i;i=nxt[i])
		if(to[i]!=pre)
			dfs(to[i],x,deep+data[i]);
}
bool dfs_flag(int x,int pre,ll deep)
{
	if(x==tar) {path[tar]=-1; dis[tar]=deep; return true;}
	for(int i=head[x];i;i=nxt[i])
		if(to[i]!=pre&&dfs_flag(to[i],x,deep+data[i]))
			{path[x]=to[i],dis[x]=deep; return true;}
	return false;
}

ll dfs_len(int x,int pre)
{
	ll mx=0;
	for(int i=head[x];i;i=nxt[i])
		if(to[i]!=pre&&!path[to[i]])
			mx=max(mx,dfs_len(to[i],x)+data[i]);
	return mx;
}
int main()
{
	scanf("%d",&n);
	for(int x,y,z,i=1;i<n;i++)
	{
		scanf("%d%d%d",&x,&y,&z);
		add(x,y,z);
		add(y,x,z);
	}
	dfs(1,0,0);
	start=tar; ans=0;
	dfs(start,0,0);
	dfs_flag(start,0,0);
	ll now=0;
	int l=start,r=tar;
	for(int i=start;i!=tar;i=path[i])
	{
		ll tmp=dfs_len(i,0);
		if(tmp==dis[i]) l=i;
		if(tmp==ans-dis[i]) {r=i; break;}
	}
	for(int i=l;i!=r;i=path[i]) fna++;
	printf("%lld\n%d",ans,fna);
	return 0;
}

发表评论

邮箱地址不会被公开。 必填项已用*标注