题意

给定一棵无根树,每次操作选两个叶子,把两者的距离加入贡献然后删掉其中一个,求最大贡献及对应方案

思路

不难发现离一个叶子节点最远的点必然是直径的一个端点,不妨删除这个叶子节点,这样既可以使破坏该节点产生的贡献最大又可以不用破坏直径。

先找出直径,然后删叶子,最后挨个删直径即可

#include <cstdio>
#include <cctype>
#define FOR(i, a, b) for (int i = a; i <= b; ++i)

inline int read()
{
    char c = getchar();
    int s = 0;
    while (!isdigit(c))
        c = getchar();
    while (isdigit(c))
        s = 10 * s + c - '0', c = getchar();
    return s;
}

const int maxn = 2e5 + 5;

int n, head[maxn], to[maxn << 1], nxt[maxn << 1], cnt;

void add(int u, int v)
{
    to[++cnt] = v;
    nxt[cnt] = head[u];
    head[u] = cnt;
    return;
}

int maxdist = 0, d1, d2;
int dis[2][maxn];

void dfs1(int u, int fa, int dist, int &d, int tag)
{
    if (dist > maxdist)
        d = u, maxdist = dist;
    if (tag > -1)
        dis[tag][u] = dist;
    for (int i = head[u]; i; i = nxt[i])
    {
        int v = to[i];
        if (v == fa) continue;
        dfs1(v, u, dist + 1, d, tag);
    }
    return;
}

int diameter[maxn], tot = 0;
long long ret = 0;

struct node
{
    int a, b, c;
    node(){}
    node(int u, int v, int w) {a = u, b = v, c = w;}
} ans[maxn];

bool dfs2(int u, int fa)
{
    bool flag = 0;
    if (u == d2)
        return diameter[++cnt] = u, 1;
    for (int i = head[u]; i; i = nxt[i])
    {
        int v = to[i];
        if (v == fa) continue;
        if (dfs2(v, u))
            diameter[++cnt] = u, flag = 1;
    }
    if (!flag)
        if (dis[0][u] > dis[1][u])
            ans[++tot] = node(u, d1, u), ret += dis[0][u];
        else
            ans[++tot] = node(u, d2, u), ret += dis[1][u];
    return flag;
}

int main()
{
    n = read();
    FOR(i, 1, n - 1)
    {
        int u = read(), v = read();
        add(u, v); add(v, u);
    }
    dfs1(1, 0, 0, d1, -1);
    maxdist = 0;
    dfs1(d1, 0, 0, d2, 0);
    dfs1(d2, 0, 0, d1, 1);
    cnt = 0;
    dfs2(d1, 0);
    FOR(i, 1, cnt - 1)
        ans[++tot] = node(diameter[i], d1, diameter[i]), ret += dis[0][diameter[i]];
    printf("%lld\n", ret);
    FOR(i, 1, tot)
        printf("%d %d %d\n", ans[i].a, ans[i].b, ans[i].c);
    return 0;
}
最后修改日期:2021年2月19日

作者

留言

撰写回覆或留言

发布留言必须填写的电子邮件地址不会公开。