[SDOI2013]直径(树的直径)
[SDOI2013]直径
题目描述
小Q最近学习了一些图论知识。根据课本,有如下定义。树:无回路且连通的无向图,每条边都有正整数的权值来表示其长度。如果一棵树有N个节点,可以证明其有且仅有N-1 条边。
路径:一棵树上,任意两个节点之间最多有一条简单路径。我们用 dis(a,b)表示点a和点b的路径上各边长度之和。称dis(a,b)为a、b两个节点间的距离。
直径:一棵树上,最长的路径为树的直径。树的直径可能不是唯一的。
现在小Q想知道,对于给定的一棵树,其直径的长度是多少,以及有多少条边满足所有的直径都经过该边。
输入输出格式
输入格式:
第一行包含一个整数N,表示节点数。 接下来N-1行,每行三个整数a, b, c ,表示点 a和点b之间有一条长度为c的无向边。
输出格式:
共两行。第一行一个整数,表示直径的长度。第二行一个整数,表示被所有直径经过的边的数量。
输入输出样例
输入样例#1: 复制
6
3 1 1000
1 4 10
4 2 100
4 5 50
4 6 100
输出样例#1: 复制
1110
2
说明
【样例说明】 直径共有两条,3 到2的路径和3到6的路径。这两条直径都经过边(3, 1)和边(1, 4)。
对于100%的测试数据:2<=N<=200000,所有点的编号都在1..N的范围内,边的权值<=10^9。
题解
这道题目比较玄学。
我们知道对一颗树不止一条直径,那么被所有直径经过的边,
随着直径的分叉而不断收缩。
而直径的分叉点一定是在直径上的。
但是我并不知道为什么要从起点推一遍收缩的一段,再从树的直径的末尾推一遍收缩的另一端。这两段之间的路径就是解。
但是一遍循环是推不出的,必须循环两次??
如果有巨佬一个循环搞出了两个段点可以告诉本蒟蒻qwq。
代码
#include<cstdio>
#include<cstring>
#include<cmath>
#include<iostream>
#include<algorithm>
#define ll long long
using namespace std;
const int N=200001;
ll dis[N],maxx,s,t;
ll n,m,num,head[N],vis[N];
struct node{
int to,nex;
ll v;
}e[N<<1];
ll dep[N],ff[N],l,r,ans,son[N];
void add(int from,int to,ll v){
num++;
e[num].to=to;
e[num].v=v;
e[num].nex=head[from];
head[from]=num;
}
ll read(){
ll x=0,w=1;char ch=getchar();
while(ch>'9'||ch<'0'){if(ch=='-')w=-1;ch=getchar();}
while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();
return x*w;
}
void dfs(int x,int fa){
for(int i=head[x];i;i=e[i].nex){
int v=e[i].to;if(v==fa)continue;ff[v]=x;
dis[v]=dis[x]+e[i].v;dfs(v,x);
}
}
void dfs2(int u,int fa){
dep[u]=0;ll maxn=0;
for(int i=head[u];i;i=e[i].nex){
int v=e[i].to;if(v==ff[u]||vis[v]==1)continue;
dfs2(v,u);maxn=max(maxn,dep[v]+e[i].v);
}
dep[u]=maxn;
}
int main(){
n=read();
for(int i=1;i<n;i++)
{
ll x=read(),y=read(),z=read();
add(x,y,z);add(y,x,z);
}
dfs(1,0);
for(int i=1;i<=n;i++){if(dis[i]>maxx)maxx=dis[i],s=i;dis[i]=0;}
dfs(s,0);maxx=0;
for(int i=1;i<=n;i++)if(dis[i]>maxx)maxx=dis[i],t=i;
printf("%lld\n",maxx);
l=t;r=s;
int now=t;
while(now!=s){
vis[now]=1;son[ff[now]]=now;now=ff[now];
}
now=t;
while(now!=s){
dep[now]=0;
dfs2(now,0);
if(dep[now]==maxx-dis[now])l=now;
now=ff[now];
}
now=s;
while(now){
dfs2(now,0);
if(dep[now]==dis[now])r=now;
now=son[now];
}
while(l!=r&&l){
l=ff[l];ans++;
}
printf("%lld\n",ans);
return 0;
}