zoukankan      html  css  js  c++  java
  • BZOJ_3124_[Sdoi2013]直径_树形DP

    BZOJ_3124_[Sdoi2013]直径_树形DP

    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。


    边权非负,可以用一个基于贪心的方法求直径。

    以1为根进行dfs,求出每个点到根的距离dis1,令rt1为最大的一个点。

    以rt1为根就能拽出来一条直径rt1---rt2,求出每个点到根的距离dis2。

    所求的那些边一定是连续的,如果不连续则中间的那个一定可以替代边上的边。

    考虑dis2[i]=dis2[rt2]的那些点i,一定可以和rt1形成又一条直径,于是可以把i和rt2的lca一下的那些边抠掉。

    然后反过来再做一遍就可以啦。

    代码:

    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    using namespace std;
    #define N 200050
    typedef long long ll;
    int head[N],to[N<<1],nxt[N<<1],val[N<<1],cnt,n,fa[N],dep[N],f[25][N],dis3[N];
    ll dis1[N],dis2[N];
    int rt1,rt2;
    inline void add(int u,int v,int w) {
        to[++cnt]=v; nxt[cnt]=head[u]; head[u]=cnt; val[cnt]=w;
    }
    void dfs1(int x,int y) {
        int i;
        if(dis1[x]>dis1[rt1]) rt1=x;
        for(i=head[x];i;i=nxt[i]) {
            if(to[i]!=y) {
                dis1[to[i]]=dis1[x]+val[i];
                dfs1(to[i],x);
            }
        }
    }
    void dfs2(int x,int y) {
        int i; fa[x]=y; f[0][x]=y;
        if(dis2[x]>dis2[rt2]) rt2=x;
        for(i=head[x];i;i=nxt[i]) {
            if(to[i]!=y) {
                dep[to[i]]=dep[x]+1;
                dis2[to[i]]=dis2[x]+val[i];
                dfs2(to[i],x);
            }
        }
    }
    int lca(int x,int y) {
        if(dep[x]<dep[y]) swap(x,y);
        int i;
        for(i=20;i>=0;i--) {
            if(f[i][x]&&dep[f[i][x]]>=dep[y]) x=f[i][x];
        }
        if(x==y) return x;
        for(i=20;i>=0;i--) {
            if(f[i][x]&&f[i][y]&&f[i][x]!=f[i][y]) x=f[i][x],y=f[i][y];
        }
        return f[0][x];
    }
    void dfs3(int x,int y) {
        int i; f[0][x]=y;
        for(i=head[x];i;i=nxt[i]) {
            if(to[i]!=y) {
                dep[to[i]]=dep[x]+1;
                dis3[to[i]]=dis3[x]+val[i];
                dfs3(to[i],x);
            }
        }
    }
    int main() {
        scanf("%d",&n);
        int i,x,y,z;
        for(i=1;i<n;i++) {
            scanf("%d%d%d",&x,&y,&z); add(x,y,z); add(y,x,z);
        } 
        dfs1(1,0);
     
        dfs2(rt1,0);
        int j;
        for(i=1;(1<<i)<=n;i++) {
            for(j=1;j<=n;j++) f[i][j]=f[i-1][f[i-1][j]];
        }
        int U=rt2;
        for(i=1;i<=n;i++) if(dis2[i]==dis2[rt2]) U=lca(U,i);
        memset(f,0,sizeof(f));
        dfs3(rt2,0);
        for(i=1;(1<<i)<=n;i++) {
            for(j=1;j<=n;j++) f[i][j]=f[i-1][f[i-1][j]];
        }
        int D=rt1;
        // printf("%d %d
    ",rt1,rt2);
        for(i=1;i<=n;i++) if(dis3[i]==dis3[rt1]) D=lca(D,i); 
        printf("%lld
    %d
    ",dis2[rt2],dep[D]-dep[U]);
    }
    
  • 相关阅读:
    Python中 sys.argv[]的用法简明解释
    python多线程
    python 多进程
    shell----bash
    linux crontab
    Elastic search 概述
    Elastic search 入门
    Elastic search CURL命令
    Elastic search 基本使用
    Elastic search 字段折叠 collaose
  • 原文地址:https://www.cnblogs.com/suika/p/9463908.html
Copyright © 2011-2022 走看看