CF963B 树的删除

1 CF963B 树的删除

2 题目描述

给你一棵树(一个有 \(n\) 个顶点和 \(n-1\) 边的图,在这个图中,可以只使用它的边从任何其他顶点到达任何顶点)。

如果这个顶点具有偶数度,则顶点可以被删除。如果删除掉一个顶点,连接到它的所有边也会被删除。

如果可能,给出删除给定树中所有顶点的顺序。

3 题解

解决这道题的切入点在于树:这道题完全可以出在正常的图上,我们为啥非得要在树上解决问题呢?我们发现,给出树后,我们在图的任意部分处理时,只要确定了节点数,一定能确定出边的数量。

由这一点出发,我们发现:任意子树可以通过节点数的奇偶性分成两种,奇树和偶树。如果整个树是一颗偶树的话,边数为奇数。由于每次只能删除偶数条边,我们肯定不能将整个树删除。如果一个子树是奇树的话,那么这个树一定由三部分构成:若干个偶树,根节点,偶数个奇树。删除时,我们必须先删除偶树,因为如果我们先删除奇树,会导致根节点被删除:奇树可能会退化成一个点的形式,要想删除这个点就必须删除根节点。而偶数在删除完毕后可能会退化成两个点形式,这时删除其中与根节点有连线的点(有 \(2\) 条 连线,为偶数条,可以删除),可以完整地将偶树删除。(注意:这里可以删除偶树,主要是借助了根节点与偶树的连边,这就是为什么单独的一个偶树无法被删除,偶子树却可以被删除)在删除完偶子树后,与根结点相连的边便只有偶数条了(每个奇子树贡献一条)。因此,我们可以把根节点删除,剩下的偶数个奇树就可以单独根据上述过程删除了。

如果我们遍历到的子树是偶子树,我们采取的删法一样,但证明过程略有不同。偶树肯定也是由三部分组成的:若干个偶树,根节点,奇数个奇树。我们在删除完偶树后,发现此时奇数个奇子树会与根节点有奇数条连线,不满足连线为偶数条这一条件。但是,我们在上面说过,如果我们删除的是偶树,那么需要借助的是根节点的帮助:加上与更上一级的根节点的连线后,根节点的连线条数成功变为偶数条,可以删除!为什么我们在上面删除奇树时就没有与更上一级的根节点的连线呢?这是因为,我们是先删除的根节点再删除的奇子树。在删除奇子树是根节点已经不复存在了。

综上所述:我们只用按照偶子树,根节点,奇子树的方法删除即可完美删除掉可以被删除的树。

4 代码(空格警告):

#include <iostream>
#include <queue>
using namespace std;
const int N = 2e5+10;
int n, p;
int tot;
int head[2*N], ver[2*N], last[2*N], sz[N];

void add(int x, int y)
{
    ver[++tot] = y;
    last[tot] = head[x];
    head[x] = tot;
}
void init(int x, int fa)
{
    for (int i = head[x]; i; i = last[i])
    {
        int y = ver[i];
        if (fa == y) continue;
        init(y, x);
        sz[x] += sz[y];
    }
}
void dfs(int x, int fa)
{
    queue<int> even, odd;
    for (int i = head[x]; i; i = last[i])
    {
        int y = ver[i];
        if (y == fa) continue;
        if (sz[y] % 2 == 0) even.push(y);
        else odd.push(y);
    }
    while (even.size())
    {
        dfs(even.front(), x);
        even.pop();
    }
    cout << x << endl;
    while (odd.size())
    {
        dfs(odd.front(), x);
        odd.pop();
    }
}
int main()
{
    cin >> n;
    for (int i = 1; i <= n; i++)
    {
        cin >> p;
        if (!p) continue;
        add(i, p);
        add(p, i);
    }
    for (int i = 1; i <= n; i++) sz[i] = 1;
    if (n % 2 == 0)
    {
        cout << "NO";
        return 0;
    }
    else cout << "YES" << '\n';
    init(1, -1);
    dfs(1, -1);
    return 0;
}

欢迎关注我的公众号:智子笔记

posted @ 2021-02-05 16:28  David24  阅读(101)  评论(0编辑  收藏  举报