zoukankan      html  css  js  c++  java
  • [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: 复制
    8
    0 1 1
    1 2 2
    2 3 3
    3 4 4
    3 7 4
    2 6 3
    1 8 1

    输出样例#1: 复制

    10
    1

    说明

    【样例说明】 直径共有两条,3 到2的路径和3到6的路径。这两条直径都经过边(3, 1)和边(1, 4)。

    对于100%的测试数据:2<=N<=200000,所有点的编号都在1..N的范围内,边的权值<=10^9。

    Soultion

    本题一看就是树的直径(题目都说了),它要求所有直径都经过的边的数量。
    先看样例的图,把直径给标出来。

    直径是1,2,3,4,7,直径长度为10,我们枚举直径上的点,只要验证是否存在一个长度,等于之前的长度就行啦。我们枚举到1,发现8可以替换0,所以8,2,3,47形成新的直径,显然1,2之间这条边不可能是重合的边,就这样推过去,可以发现只有中间的边是重合的边,但是我们需要判断当前这个点是往左走,还是往右走,例如8这个点,显然走往3,不会往1走,6是往4走,5是往3走。
    代码如下:

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    using namespace std;
    typedef long long lol;
    struct node
    {
        lol to,next,w;
    }a[500100];
    lol len,last[500010],fa[501010],vis[501001],ans[501010],d[501010],l,r;
    lol rr,ll;
    void add(lol a1,lol a2,lol a3)
    {
        a[++len].to=a2;
        a[len].w=a3;
        a[len].next=last[a1];
        last[a1]=len;
    }
    void dfs(lol x,lol father)
    {
        for(lol i=last[x];i;i=a[i].next)
        {
            lol to=a[i].to;
            if(to==father||vis[to]) continue;
            fa[to]=x;
            d[to]=d[x]+a[i].w;
            dfs(to,x);
        }
    }
    void dfs1(lol x,lol father,lol id)
    {
        for(lol i=last[x];i;i=a[i].next)
        {
            
            lol to=a[i].to;
            if(to==father||vis[to]) continue;
            d[to]=d[x]+a[i].w;
            if(d[to]==ans[id]||d[to]==ans[r]-ans[id])
            rr=id;
            dfs1(to,x,id);
        }
    }
    void dfs2(lol x,lol father,lol id)
    {
        for(lol i=last[x];i;i=a[i].next)
        {
            
            lol to=a[i].to;
            if(to==father||vis[to]) continue;
            d[to]=d[x]+a[i].w;
            if(d[to]==ans[id]||d[to]==ans[r]-ans[id])
            if(!ll)
            ll=id;
            dfs2(to,x,id);
        }
    }
    int main()
    {
        lol n,x,y,z;
        cin>>n;
        for(lol i=1;i<n;i++)
        {
            scanf("%lld%lld%lld",&x,&y,&z);
            add(x,y,z);add(y,x,z);
        }
        dfs(1,0);
        for(lol i=1;i<=n;i++) if(d[i]>d[l]) l=i;
        memset(fa,0,sizeof(fa));memset(d,0,sizeof(d));
        dfs(l,0);
        for(lol i=1;i<=n;i++) if(d[i]>d[r]) r=i;
        for(lol i=r;i;i=fa[i]) vis[i]=1,ans[i]=d[i];
        cout<<d[r]<<endl;
        memset(d,0,sizeof(d));
        for(lol i=fa[r];fa[i]!=0;i=fa[i])
        {
            if(ans[i]*2>ans[r])
            dfs1(i,0,i);
            else
            dfs2(i,0,i);
        }
        lol pp=0,p=0;
    //	cout<<ll<<' '<<rr<<endl;
        for(lol i=r;fa[i];i=fa[i])
        p++;
        if(ll==0)
        {
            for(int i=rr;fa[i]!=0;i=fa[i])
            pp++;
            cout<<pp<<endl;
        }
        else if(rr==0)
        {
            for(int i=r;i!=ll;i=fa[i])
            pp++;
            cout<<pp;
        }
        else
        {
            for(int i=rr;i!=ll;i=fa[i])
            pp++;
            cout<<pp<<endl;
        }
    }
    

    博主蒟蒻,可以随意转载,但必须附上原文链接k-z-j

  • 相关阅读:
    迭代器、生成器、内置函数
    函数
    文件操作
    使用 java 做爬虫的简单例子
    关于 spring 使用 mongodb 的 mongotemplate 对象操作数据库,对象注入问题(即该对象能否正常的调用相应的CRUD方法来处理数据)
    eclipse 使用 “全局搜索” 失灵的解决方法
    使用 Qrcode 生成中间带 logo 的二维码!
    将 BufferedImage 对象图片,转成 Base64 编码给前端<img src="编码"/>展示图片用
    使用 Qrcode 生成二维码
    链接转化成二维码
  • 原文地址:https://www.cnblogs.com/kzj-pwq/p/9502877.html
Copyright © 2011-2022 走看看