3124: [Sdoi2013]直径【dfs】【树的直径】【外向树】

**

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

【样例说明】

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

对于100%的测试数据:2≤N≤200000,所有点的编号都在1..N的范围内,

边的权值≤10^9。

**

Source

**

**

Solution

**
做了bzoj1999差不多就可以想到这种做法了

先两边dfs找直径 然后做外向树【是叫这个吧……】

然后从一头跑呀跑~

如果当前遍历的节点的外向树上存在一条路径满足 到已经直径一段的距离相等 就说明那个端点也是直径

这里写图片描述

[没有节点的外向树 有节点的那条是直径]

比如我们遍历到了第三个节点 有两个外向树 如果至少有一个满足上述条件 那么之前经过的那些边一定不是所有直径经过的【想一想为什么?】


想明白上面的看下面的


直到一个mid!
然后我们在mid左面找到一个节点就跳出循环 输出答案就好了

至于为什么是long long
我会告诉你我WA了么……然后找了个std对拍……并没有什么发现……然后……

#include <bits/stdc++.h>
using namespace std;
#define ll long long
const int maxn=4e5+10;

struct data{ll to,next,w;}e[maxn*2];
ll head[maxn],cnt;
void ins(ll u,ll v,ll w){cnt++;e[cnt].to=v;e[cnt].next=head[u];head[u]=cnt;e[cnt].w=w;}
void insert(ll u,ll v,ll w){ins(u,v,w);ins(v,u,w);}
ll fa[maxn],last,end,st,ed,root,mx;
ll belong[maxn],ans,n;
bool vis[maxn];
long long dis[maxn];
void dfs(ll x)
{
    vis[x]=1;
    for(ll i=head[x];i;i=e[i].next)
        if(!vis[e[i].to])
        {
            dis[e[i].to]=dis[x]+e[i].w;
            if(mx<dis[e[i].to])
            {
                mx=dis[e[i].to];
                root=e[i].to;
            }
            fa[e[i].to]=x;
            dfs(e[i].to);
        }
    vis[x]=0;
}
int main()
{
    //freopen("in.txt","r",stdin);
    scanf("%lld",&n);
    for(ll i=1;i<n;i++)
    {
        ll u,v,w;
        scanf("%lld%lld%lld",&u,&v,&w);
        insert(u,v,w);
    }
    dfs(1);st=root;mx=0;dis[st]=0;fa[st]=0;dfs(st);ed=root;
    for(ll i=ed;i;i=fa[i])vis[i]=1;fa[0]=0;
    for(ll i=ed;i;i=fa[i])
    {
        if(dis[i]==dis[ed]-dis[i])belong[i]=-1;
        else belong[i]=(dis[i]<dis[ed]-dis[i])?st:ed;
        long long len=dis[i];
        dis[i]=0;mx=root=0;dfs(i);dis[i]=abs(dis[belong[i]]-len);vis[i]=1;
        if(dis[root]==dis[i])
        {
            if(belong[i]==-1){printf("0");return 0;}
            if(belong[i]==ed)last=i;
            else {end=i;break;}
        }
        dis[i]=len;
    }
    if(!last)last=ed;
    if(!end)end=st;
    for(ll i=last;i!=end;i=fa[i])
        ans++;
    cout<<dis[ed]<<endl<<ans;
    return 0;
}


——生且不惧,死又何妨

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值