SDOI2013直径(树的直径)

题目描述:

点这里

 

题目大意:

就是在一个树上找其直径的长度是多少,以及有多少条边满足所有的直径都经过该边。

 

题解:

首先,第一问很好求,两边dfs就行了,第一次从任一点找距它最远的点,再从这个点找距它的最远点,后两个点就是树的直径的两个端点,证明就不赘述了,有兴趣可以自己证一证玩一玩。

那第二问怎么办呢?假设我们有这样一个图(如下)

 

如图所示,中间那根直的就是树的直径之一,旁边标红的也是树的直径。(图画的不好,感性理解)

我们要知道,树的直径是必定会有交叉的,可以画个图自己看一下。

所以就会有一个想法:首先找出一条直径的起点,向终点推,如果碰到交叉,就看这个交叉是否是直径,如果是,就把第一个直径收缩,再继续找。再从终点向起点收缩一遍。剩下的边就是题目中要求的了。

最后就是代码实现了,收缩的过程是真滴玄学。

代码如下:

#include<iostream>
#include<queue>
#include<cmath>
#include<cstdio>
#include<vector>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#define ll long long 
#define rint register int
#define M 200005
using namespace std;
inline int read()
{
    int s=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){s=s*10+ch-'0';ch=getchar();}
    return s*f;
}
inline ll max(ll a,ll b){return a>b?a:b;}
ll dis[M],maxx,s,t;
ll n,m,cnt,head[M],vis[M];
ll dep[M],father[M],l,r,ans,son[M];
struct edge
{
    int to,nex,v;       
}e[M<<1];
inline void add(int u,int v,int w)
{
    e[++cnt].to=v;
    e[cnt].v=w;
    e[cnt].nex=head[u];
    head[u]=cnt;
}
void dfs(int u,int fa)
{
    for(rint i=head[u];i;i=e[i].nex)
    {
        int v=e[i].to;if(v==fa) continue;father[v]=u;
        dis[v]=dis[u]+e[i].v;dfs(v,u);
    }
}
void find(int u,int fa)
{
    dep[u]=0;ll maxn=0;
    for(rint i=head[u];i;i=e[i].nex)
    {
        int v=e[i].to;if(v==father[u] || vis[v]==1) continue;
        find(v,u);maxn=max(maxn,dep[v]+e[i].v);
    }
    dep[u]=maxn;
}
int main()
{
    n=read();
    for(rint i=1;i<=n-1;++i)
    {
        int x=read(),y=read(),z=read();
        add(x,y,z),add(y,x,z);
    }
    dfs(1,0);
    for(rint i=1;i<=n;++i)
    {
        if(dis[i]>maxx) maxx=dis[i],s=i;
        dis[i]=0;
    }
    dfs(s,0);maxx=0;
    for(rint i=1;i<=n;++i)
    {
        if(dis[i]>maxx) maxx=dis[i],t=i;
    }
    printf("%lld\n",maxx);
    int l=t,r=s,now=t;
    while(now!=s)
    {
        vis[now]=1;
        son[father[now]]=now;
        now=father[now];
    }
    now=t;
    while(now!=s)
    {
        dep[now]=0;
        find(now,0);
        if(dep[now]==maxx-dis[now]) l=now;
        now=father[now];
    }
    now=s;
    while(now)
    {
        find(now,0);
        if(dep[now]==dis[now]) r=now;
        now=son[now];
    }
    while(l!=r && l)
    {
        l=father[l];
        ++ans;
    }
    printf("%lld\n",ans);
    return 0;
}
View Code

谢谢大家!

转载于:https://www.cnblogs.com/mxrmxr/p/10049410.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值