bzoj3124 [Sdoi2013]直径 树的直径

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

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

输出

共两行。第一行一个整数,表示直径的长度。第二行一个整数,表示被所有
直径经过的边的数量。
样例输入
6
3 1 1000
1 4 10
4 2 100
4 5 50
4 6 100
样例输出
1110
2
【样例说明】
直径共有两条,3 到2的路径和3到6的路径。这两条直径都经过边(3, 1)和边(1, 4)。
提示
对于100%的测试数据:2≤N≤200000,所有点的编号都在1..N的范围内,
边的权值≤10^9。

树的直径求起来挺容易,两边bfs。
这题有意思在于第二问。明显树的直径不只有一条,但直径间至少会有1个点相交。设当前已经确定了一条直径,然后从直径的一段(设为x),枚举到另一端(y)。并维护l,r(所有直径相交的部分从l点到r点)
如果当前节点i与x的距离==i在不经过直径时能到达的最大距离,那么说明直径在这里产生分支,把l设为i.
如果i到y的距离==i在不经过直径时能到达的最大距离,同样说明直径产生了分支,r=i,跳出即可。

#include 
#include 
#include 
#include 
#include 
#define N 200010
#define ll long long
using namespace std;
int read()
{
    int sum=0,f=1;char x=getchar();
    while(x<'0'||x>'9')f=(x=='-')?-1:1,x=getchar();
    while(x>='0'&&x<='9')sum=(sum<<3)+(sum<<1)+x-'0',x=getchar();
    return sum*f;
}
struct road{int v,next;ll l;}lu[N*2];
int n,e,adj[N],rt1,rt2,q[N],h,t,from[N],hh[N];
bool vis[N];ll dis[N];
inline void gmax(ll &x,ll y){if(y>x)x=y;}
inline void bfs(int S)
{
    memset(vis,0,sizeof(vis));
    dis[S]=0;vis[S]=1;h=1;t=0;q[++t]=S;
    while(h<=t)
    {
        int x=q[h++];
        for(int i=adj[x];i;i=lu[i].next)
        {
            int to=lu[i].v;if(vis[to])continue;
            dis[to]=dis[x]+lu[i].l;
            vis[to]=1;from[to]=x;
            q[++t]=to;
        }
    }
}
inline ll dfs(int x,int fa)
{
    if(fa!=0&&vis[x])return 0;
    ll s=0;
    for(int i=adj[x];i;i=lu[i].next)
    {
        int to=lu[i].v;if(to==fa||vis[to])continue;
        gmax(s,dfs(to,x)+lu[i].l);
    }
    return s;
}
int main()
{
    n=read();
    for(int i=1,x,y,l;ix=read();y=read();l=read();
        lu[++e]=(road){x,adj[y],l};adj[y]=e;
        lu[++e]=(road){y,adj[x],l};adj[x]=e;
    }
    ll s=0;int x,l,r;
    bfs(1);
    for(int i=1;i<=n;i++)if(ss=dis[i],rt1=i;
    bfs(rt1);s=0;
    for(int i=1;i<=n;i++)if(ss=dis[i],rt2=i;
    memset(vis,0,sizeof(vis));
    x=rt2;vis[rt1]=1;
    while(x!=rt1)
    {
        vis[x]=1,hh[from[x]]=hh[x]+1;
        x=from[x];
    }
    l=rt2;r=rt1;x=rt2;
    while(x!=rt1)
    {
        s=dfs(x,0);
        if(s==dis[rt2]-dis[x])l=x;
        if(s==dis[x]){r=x;break;}
        x=from[x];
    }
    printf("%lld\n%d",dis[rt2],hh[r]-hh[l]);
}

你可能感兴趣的:(图论,赛前集训,达哥的杂题)