舒阳 的博客

舒阳 的博客

QAQ QWQ

题解 P1268 【树的重量】

posted on 2019-11-03 14:25:09 | under 题解 |

思路

考虑根据这个邻接矩阵,如何生成一棵树。 先将点$1-2$连边,并将距离算进最终答案,这样构成了一个只有两个节点的树。

见例图:(黑色为还未加入树中, 红色为加入了, 字母点为非叶子节点,数字点为叶子节点) 原图

加入两个点$1-2$,最终答案加上($2 - A - B - 1$)的长度 KOqn3R.png

加入点$3$,最终答案再加上($A - C - D - 3$)的长度 KOqKjx.png

加入点$4$,最终答案再加上($D - 4$)的长度 KOqQu6.png

加入点$5$,最终答案加上($C - 5$)的长度 KOqug1.png

如此往复,最终加入全部的点 KOLtdU.png

也就是每次加点的时候,只需要把它和最近的一个已经在树上的点相连即可。这条路径的长度需要加入最终答案。

难点在于,如何确定每次加点需要累计的答案。离当前待加入的点 最近的一个树中点一定不是一个叶子节点,

但是我们只有每个叶子节点之间的最短距离。

观察下图

KXV0sK.png

$1 ,2$都已经在树中了,$3$不在树中,要在最终答案中加入$A - 3$的长度,可以发现:

$$dis(A, 3) = (dis(2, 3) + dis(1, 3) - dis(1, 2)) / 2$$

也就是我们枚举两个在树中的点对,通过上面的式子,得到加入这个点的价值,这些价值中取最小值即可。

这样就可以算出答案。

我们按顺序加入点,如果当前要加入的点为点$i$,那就要考虑每一个已加入的点$1$-$(i - 1)$组成的点对。

同时可以发现,只需要从枚举$1$到剩余其他树中的点即可。

Codes

这题代码的话,我觉得唯一的难点在于如何读入……

/*!
 * Copyright(c) 2019 Shu_Yu_Mo
 * MIT Licensed
 * Luogu: https://www.luogu.org/space/show?uid=44615
 * Github: https://github.com/oldsuold/
 * Gitee: https://gitee.com/Shu_Yu_Mo/
 */
#include<cstdio>
#include<cstring>
#include<cmath>
#include<cstring>
#include<iostream>
#include<cmath>
#include<vector>
#include<set>
#include<algorithm>
#define inf 0x7fffffff
#define _R register
using namespace std;
const int _ = 110;
inline int read()
{
    char c = getchar(); int sign = 1; int x = 0;
    while(c > '9' || c < '0') { if(c=='-')sign = -1; c = getchar(); } 
    while(c <= '9' && c >= '0') { x *= 10; x += c - '0'; c = getchar(); }
    return x * sign;
}
int M[_][_];
int n;
void doit()
{
    memset(M, 0, sizeof(M));
    for(_R int i = 1;i <= n;i++){
        for(_R int j = 1;j <= n - i;j++){
            M[i][j + i] = M[j + i][i] = read();
        }
    }
    int ans = M[1][2];
    for(_R int i = 3;i <= n;i++){
        int TMP = inf;
        for(_R int j = 1;j < i;j++)
            TMP = min(TMP, ((M[1][i] + M[j][i] - M[1][j]) >> 1));
        ans += TMP;
    }
    printf("%d\n", ans);
}       
int main()
{
    while((n = read()) != 0) doit();
    return 0;
}