CF1521D Nastia Plays with a Tree 题解

一、题目:

codeforces原题

洛谷原题

二、思路:

这是一道非常清奇的贪心题。

考虑这样一件事情,假设我们切了\(x\)刀后把整棵树变成了一堆链,那么链的个数一定是\(x+1\)

这样的话,我们只需要将这些链首尾相连,最终形成的树也一定是一条链,而首尾相连的操作次数也一定是\(x\)

我们又可以发现,先切哪一刀和先连哪一条边是无所谓的。我们只需要关心哪些边需要被切来使得切的次数最小。

考虑贪心。首先我们任选一个点作为根。然后假设我们现在要处理以\(v\)为根的子树。

  1. 递归处理\(v\)的所有儿子。
  2. \(v\)现在有\(c\)个儿子,对\(c\)进行分类讨论。
    1. \(c\leq 1\),啥都不需要干。
    2. \(c=2\),切断\(v\)\(v\)的父亲(如果有的话)。
    3. \(c\ge 3\),先切断\(v\)\(v\)的父亲(如果有的话),然后随便切断\(v\)\(c-2\)个儿子。

现在我们来感性理解一下为什么这样贪心是对的。当执行完第1步后,\(v\)的所有儿子变成了一条链的一端。对于\(v\)来说,我们需要尽可能使得\(v\)的父亲的儿子数减少,这是因为一个点的儿子数越少,操作数也会变得越少。所以当有必要时,首先考虑切\(v\)的父亲 ,再切\(v\)的儿子。

三、代码:

#include <iostream>
#include <cstdio>
#include <cstring>

using namespace std;

inline int read(void) {
    int x = 0, f = 1; char ch = getchar();
    while (ch < '0' || ch > '9') { if (ch == '-') f = -1; ch = getchar(); }
    while (ch >= '0' && ch <= '9') { x = x * 10 + ch - '0'; ch = getchar(); }
    return f * x;
}

const int maxn = 1e5 + 5;

int T, n, head[maxn], tot, top, deg[maxn];
bool vis[maxn];

pair<int, int>pa[maxn];

struct Edge {
    int y, next;
    bool tag;
    Edge() {}
    Edge(int _y, int _next, bool _tag) : y(_y), next(_next), tag(_tag) {}
}e[maxn << 1];

struct Opt{
    int x1, y1, x2, y2;
}opt[maxn];

inline void connect(int x, int y) {
    e[++ tot] = Edge(y, head[x], 1);
    head[x] = tot;
}

void dfs(int x, int fa) {
    int pos = 0;
    for (int i = head[x]; i; i = e[i].next) {
        int y = e[i].y;
        if (y == fa) { pos = i; continue; }
        dfs(y, x);
    }
    int cnt = 0;
    for (int i = head[x]; i; i = e[i].next) {
        int y = e[i].y;
        if (y == fa || !e[i].tag) continue;
        ++ cnt;
    }
    if (cnt <= 1) return;
    if (cnt == 2) {
        if (!fa) return;
        e[pos].tag = e[pos ^ 1].tag = false;
        opt[++ top].x1 = x; opt[top].y1 = fa;
        -- deg[x]; -- deg[fa];
        return;
    }

    if (fa) {
        e[pos].tag = e[pos ^ 1].tag = false;
        opt[++ top].x1 = x; opt[top].y1 = fa;
        -- deg[x]; -- deg[fa];
    }
    int tmp = 0;
    for (int i = head[x]; i; i = e[i].next) {
        int y = e[i].y;
        if (y == fa || !e[i].tag) continue;
        ++ tmp;
        if (tmp <= cnt - 2) {
            e[i].tag = e[i ^ 1].tag = false;
            opt[++ top].x1 = x; opt[top].y1 = y;
            -- deg[x];
            -- deg[y];
        }
    }
}

void dfs2(int x, int fa, int &to) {
    int cnt = 0;
    vis[x] = true;
    for (int i = head[x]; i; i = e[i].next) {
        int y = e[i].y;
        if (y == fa || !e[i].tag) continue;
        dfs2(y, x, to);
        ++ cnt;
    }
    if (!cnt) to = x;
}

int main() {
    T = read();
    while (T --) {
        tot = 1;
        memset(head, 0, sizeof head);
        memset(deg, 0, sizeof deg);
        memset(vis, false, sizeof vis);

        n = read();
        for (int i = 1; i < n; ++ i) {
            int a = read(), b = read();
            connect(a, b); connect(b, a);
            ++ deg[a]; ++ deg[b];
        }
        top = 0;
        dfs(1, 0);
        printf("%d\n", top);

        top = 0;
        for (int x = 1; x <= n; ++ x) {
            if (deg[x] == 0 && !vis[x]) {
                vis[x] = true;
                pa[++ top] = make_pair(x, x);
            }
            if (deg[x] == 1 && !vis[x]) {
                int y = 0;
                dfs2(x, 0, y);
                pa[++ top] = make_pair(x, y);
            }
        }
        -- top;
        for (int i = 1; i <= top; ++ i) {
            opt[i].x2 = pa[i].second;
            opt[i].y2 = pa[i + 1].first;
            printf("%d %d %d %d\n", opt[i].x1, opt[i].y1, opt[i].x2, opt[i].y2);
        }
    }
    return 0;
}

posted @ 2021-05-09 19:39  蓝田日暖玉生烟  阅读(126)  评论(0编辑  收藏  举报