Acdream Cut 贪心

题意:给定一棵树(不一定是二叉树),现在问最多能够删除多少条边使得分开的结点满足分开子树的结点数都是偶数。

思路:首先用邻接表建立起一棵树,然后我们得出以下的结论:对于任意结点而言,如果该节点能够找出和与之相连的部分子树构成偶数个点并且这偶数个点不能再分解出偶数个结点的分支出来,那么这个点与其他相连的子树(没有加入前面的集合中)的边全部断掉。之所以可以断掉这些边是因为一个我们去掉的点集都是不能够再进行分割的集合,而且又达到了偶数个点,那么这偶数个点对于其他集合肯定也是没有帮助的,因为%2之后的贡献率为0。

代码如下:

#include <iostream>
#include <cstdlib>
#include <cstring>
#include <cstdio>
using namespace std;

struct Node {
    int x, next;
}e[200005];

int N, head[100005], idx, ret, sum[100005], hash[100005];

void insert(int x, int y) {
    ++idx;
    e[idx].x = y;
    e[idx].next = head[x];
    head[x] = idx;
}

int update(int x) {
    hash[x] = 1;
    if (head[x] == -1) {
        return sum[x] = 1;
    } else {
        sum[x] = 1;
        for (int i = head[x]; i != -1; i = e[i].next) {
            if (!hash[e[i].x]) {
                sum[x] += update(e[i].x);
            }
        }
        if (!(sum[x] & 1)) ++ret;
        return sum[x];
    }
}

int main() {
    int ans;
    while (scanf("%d", &N) == 1) {
        memset(head, 0xff, sizeof (head));
        memset(hash, 0, sizeof (hash));
        int x, y;
        idx = ret = -1;
        for (int i = 1; i < N; ++i) {
            scanf("%d %d", &x, &y); 
            insert(x, y); // 对这个树进行构边
            insert(y, x); // 构建的双向边
        }
        update(1);
        printf("%d\n", ret);
    }
    return 0;
}

你可能感兴趣的:(cut)