zoukankan      html  css  js  c++  java
  • [洛谷P3304] [SDOI2013]直径

    洛谷题目链接:[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:

    6
    3 1 1000
    1 4 10
    4 2 100
    4 5 50
    4 6 100

    输出样例#1:

    1110
    2

    说明

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

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

    一句话题意: 给出一颗树,要求出树上所有直径相交的长度.

    题解: 首先处理出树上的任意一条直径,然后处理出直径上的点到它的子树(不包含在直径上的点)中最深的距离.如果这个距离和它到直径一端的距离相同的话,就说明它的子树中含有直径中的一段.那么直径的这一段相同的长度都是不能算入最终全部直径相交的长度中的.

    那么做法就可以得出了:确定一条直径,对直径上的每一个点判断它子树中是否存在有直径的一部分,如果有,那么就把指针向另一边移动,最后左右指针相减就是答案了.

    最后答案要开long long

    #include<bits/stdc++.h>
    using namespace std;
    const int N = 200000+5;
    typedef int _int;
    #define int long long
    
    int n, last[N], ecnt = 0;
    int pre[N], dep[N], fa[N], f[N], len = 0, L, R, vis[N], node[N], cnt = 0, mx[N], dist[N], ansl, ansr;
    
    struct edge{
        int to, nex, w;
    }e[N*2];
    
    void add(int x, int y, int z){
        e[++ecnt].to = y, e[ecnt].w = z, e[ecnt].nex = last[x], last[x] = ecnt;
    }
    
    void dfs(int x, int las, int deep){
        dep[x] = deep, fa[x] = las;
        for(int to, i=last[x];i;i=e[i].nex){
            to = e[i].to; if(to == las) continue;
            dfs(to, x, deep+1);
            if(len < f[x]+f[to]+e[i].w) len = f[x]+f[to]+e[i].w, L = pre[x], R = pre[to];
            if(f[x] < f[to]+e[i].w) f[x] = f[to]+e[i].w, pre[x] = pre[to];
        }
    }
    
    void get(int x, int lca){
        if(x == lca){ node[++cnt] = x; return; }
        get(fa[x], lca), node[++cnt] = x;
    }
    
    int get_dis(int x, int f){
        int res = 0;
        for(int to, i=last[x];i;i=e[i].nex){
            to = e[i].to; if(to == f || vis[to]) continue;
            res = max(res, e[i].w+get_dis(to, x));
        }
        return res;
    }
    
    void solve(){
        int x = L, y = R, lca;
        if(dep[x] < dep[y]) swap(x, y);
        while(dep[x] > dep[y]) vis[x] = 1, x = fa[x];
        if(x == y) vis[x] = 1, lca = x;
        else {
            while(x != y) vis[x] = vis[y] = 1, x = fa[x], y = fa[y];
            vis[x] = 1, lca = x;
        }
        x = L, y = R;
        while(x != lca) node[++cnt] = x, x = fa[x]; get(y, lca);
        for(int i=1;i<=cnt;i++) mx[node[i]] = get_dis(node[i], -1);
        for(int i=2;i<=cnt;i++)
    	for(int j=last[node[i]];j;j=e[j].nex)
    	    if(e[j].to == node[i-1]) dist[node[i]] = dist[node[i-1]]+e[j].w;
        ansl = 1, ansr = cnt;
        for(int i=2;i <= cnt;i++)
            if(dist[node[i]] == mx[node[i]]) ansl = i;
        for(int i=cnt-1; i >= 1 && ansr >= ansl; i--)
            if(dist[node[cnt]]-dist[node[i]] == mx[node[i]]) ansr = i;
    }
    
    _int main(){
        ios::sync_with_stdio(false);
        int x, y, z; cin >> n;
        for(int i=1;i<=n;i++) pre[i] = i;
        for(int i=1;i<n;i++) cin >> x >> y >> z, add(x, y, z), add(y, x, z);
        dfs(1, -1, 1), solve();
        cout << len << endl << ansr-ansl << endl;
        return 0;
    }
    
  • 相关阅读:
    走向灵活软件之路——面向对象的六大原则
    StartUML破解
    非常实用的Android Studio快捷键
    Android Studio更新失败
    《Effect Java》学习笔记1———创建和销毁对象
    使用spring单元调试出错initializationError
    Spring注入的不同方式
    DNS域名解析的过程
    浏览器的缓存机制
    Http建立连接的方式
  • 原文地址:https://www.cnblogs.com/BCOI/p/9523869.html
Copyright © 2011-2022 走看看