Codevs1700施工方案第二季题解

题目

  • 来源

    http://codevs.cn/problem/1700/

  • 题目描述 Description
    c国边防军在边境某处的阵地是由n个地堡组成的。工兵连受命来到阵地要进行两期施工。
    第一期的任务是挖掘暗道让所有地堡互联互通。现已勘测设计了m条互不相交的暗道挖掘方案,如果这m条暗道都实施挖掘,肯定能达到互联互通的目的。事实上,适当选择其中n-1个方案挖掘,就能实现互联互通,即从每个地堡出发都能到达其他任何一个地堡(允许经过别的地堡)。
    连长精心谋算,在m个设计规划中选取了挖掘总距离最短且能保证互联互通的若干个暗道规划实施了挖掘,完成了第一期的施工任务后又接受了第二期的施工任务,要求选择一个地堡进行扩建改造,使其能向每个地堡提供弹药。为了让弹药供应更及时、更快捷,从改扩建的地堡到最远地堡的距离(称为最远输送距离)应当尽量小。
    你的任务是先求出第一期施工挖掘的总距离,再求改扩建地堡最远输送距离的最小值。

  • 输入描述 Input Description
    其中第一行是n和m,m n
    下面的m行每行3个数 xiyizi ,表示xi到yi的距离是 zi
    zi<1000000且m个距离互不相等

  • 输出描述 Output Description
    共包含两行,每行一个整数,第一行是第一期的挖掘总距离,第二行是最远输送距离的最小值。

  • 样例输入 Sample Input
    4 5
    1 2 1
    2 3 2
    3 4 3
    4 1 4
    3 1 5

  • 样例输出 Sample Output
    6
    3

  • 数据范围及提示 Data Size & Hint
    【样例说明】
    第一期挖掘1到2、2到3和3到4的3条暗道,第二期选择3号地堡进行改扩建,最远输送距离是3
    【数据规模】
    60%的数据 n<10且m<20
    80%的数据 n<1000且m<2000
    100%的数据 n<100000且m<200000

题解

  • 第一问很简单,是很明显的最小生成树,用Kruskal算法可得到最小生成树的形态;
  • 第二问,本质上是求最小生成树的重心,使用树形DP解决:
    1、求出每个点向下走的最大深度和次大深度;
    2、借助1求出每个点向上走一步然后要么向上要么向其兄弟走的最大长度,简称为向上的最大长度;
    每个点向下的最大深度和向上的最大长度取最大值,在所有点的最大值中,值最小的点即为树的重心,这个值就是第二问的解。
  • Code
#include 
#include 
#include 
#define N 100005
#define M 400005
#define oo 1000000000
#define nil 0
using namespace std;
int n, m;
int u[M], v[M], w[M], nxt[M], pnt[N], e;
int fa[N];
inline int find(int x) { return x == fa[x] ? x : fa[x] = find(fa[x]); }
int up[N], down[N][2], dis[N];
bool vis[N];
inline void add(int a, int b, int c)
{
    u[++e] = a; v[e] = b; w[e] = c;
    nxt[e] = pnt[a]; pnt[a] = e;
}
struct node
{
    int x, y, z;
    node(int x = 0, int y = 0, int z = 0) :x(x), y(y), z(z) {}
    inline bool operator < (const node& b) const
    {
        return z < b.z;
    }
}E[M >> 1];
inline void read(int &a)
{
    char ch = getchar();
    a = 0;
    while(ch < '0' || ch > '9') ch = getchar();
    while(ch >= '0' && ch <= '9')
    {
        a = a * 10 + ch - 48;
        ch = getchar();
    }
}
void dfs1(int rot)
{
    vis[rot] = true;
    for(int i = pnt[rot]; i != nil; i = nxt[i])
    {
        if(!vis[v[i]]) dfs1(v[i]);
        else continue;
        if(down[v[i]][0] + w[i] > down[rot][0])
        {
            down[rot][1] = down[rot][0];
            down[rot][0] = down[v[i]][0] + w[i];
        }
        else if(down[v[i]][0] + w[i] > down[rot][1])
        {
            down[rot][1] = down[v[i]][0] + w[i];
        }
    }
}
void dfs2(int rot, int fat, int val)
{
    vis[rot] = true;
    int tmp = 0;
    if(rot == 1) up[rot] = 0;
    else
    {
        up[rot] = up[fat] + val;
        if(down[fat][0] == down[rot][0] + val) tmp = down[fat][1] + val;
        else tmp = down[fat][0] + val;
        up[rot] = max(up[rot], tmp);
    }
    dis[rot] = max(up[rot], down[rot][0]);
    for(int i = pnt[rot]; i != nil; i = nxt[i]) if(!vis[v[i]])
    {
        dfs2(v[i], rot, w[i]);
    }
}
int main()
{
    long long ans = 0;
    read(n); read(m);
    for(int i = 1; i <= m; ++i)
    {
        read(E[i].x); read(E[i].y); read(E[i].z);
    }
    for(int i = 1; i <= n; ++i) fa[i] = i;
    sort(E + 1, E + m + 1);
    for(int i = 1, cnt = 1; i <= m && cnt < n; ++i)
    {
        int a = find(E[i].x), b = find(E[i].y);
        if(a != b)
        {
            fa[a] = b;
            ++cnt;
            add(E[i].x, E[i].y, E[i].z);
            add(E[i].y, E[i].x, E[i].z);
            ans += E[i].z;
        }
    }
    printf("%lld\n", ans);
    memset(vis, 0, sizeof(vis));
    dfs1(1);
    memset(vis, 0, sizeof(vis));
    dfs2(1, 0, 0);
    ans = oo;
    for(int i = 1; i <= n; ++i) ans = min(ans, (long long)dis[i]);
    printf("%lld\n", ans);
    return 0;
}

你可能感兴趣的:(OI党坚毅的步伐,图结构,树结构,动态规划)