洛谷P4408 逃学的小孩

会有一股神秘感。 提交于 2019-12-02 03:14:15

题目

求树的直径,因为任意两个居住点之间有且只有一条通路,所以这是一棵树。

根据题意父母先从C去A,再去B,或者反过来。

我们一定是要让A到B最大,也要让C到A和B的最小值最大。

AB最大一定就是直径了。

CA最大直接先求出任意一条直径的两个端点,必定一个是A、一个是B。然后枚举C,找到最大的\(min(CA,CB)\)然后与AB相加即可。

#include <bits/stdc++.h>
#define int long long  
#define N 1010011
using namespace std;
struct edg {                
    int from, to, nex, len;// vis表示是否在最大生成树上 
}e[N * 2];      
int n, m, cnt, ans, ansk, lin[N], dis1[N], vis[N];// 这必是一颗树
int disk[N];
inline void add(int f, int t, int c)
{
    e[++cnt].nex = lin[f];
    e[cnt].to = t;
    e[cnt].len = c;
    lin[f] = cnt;
}
void dfs(int now)
{
    vis[now] = 1;
    for (int i = lin[now]; i; i = e[i].nex)
    {
        int to = e[i].to;
        if (!vis[to])
        {
            dis1[to] = dis1[now] + e[i].len;
            ans = max(ans, dis1[to]);
            dfs(to);
        }           
    }
}
void dfs2(int now)
{
    vis[now] = 1;
    for (int i = lin[now]; i; i = e[i].nex)
    {
        int to = e[i].to;
        if (!vis[to])
        {
            disk[to] = disk[now] + e[i].len;
            if (disk[to] > ans)
                ans = max(ans, disk[to]), ansk = to;
            dfs2(to);
        }           
    }
}
signed main()
{
    scanf("%lld%lld", &n, &m);
    for (int i = 1, a, b, c; i <= m; i++)
    {
        scanf("%lld%lld%lld", &a, &b, &c);
        add(a, b, c);
        add(b, a, c);
    }
    dfs(1);
    int maxn = 0, maxk = 0; 
    for (int i = 2; i <= n; i++)
    {
        if (dis1[i] > maxn)
        {
            maxn = dis1[i];
            maxk = i;
        } 
    }
    ans = 0; 
    maxn = 0;
    memset(vis, 0, sizeof(vis));
    dfs2(maxk);
    memset(vis, 0, sizeof(vis));
    memset(dis1, 0, sizeof(dis1));
    dfs(ansk);
    for (int i = 1; i <= n; i++)
    {
        if (i == maxk || i == ansk) continue;
        maxn = max(maxn, min(dis1[i], disk[i]));
    }
    printf("%lld", maxn + ans);//ans是树的直径大小。
}

/*
4 3
1 2 1
1 3 1
1 4 2
5
*/
标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!