HDU 5927 Auxiliary Set dfs(思维)

题目:

http://acm.hdu.edu.cn/showproblem.php?pid=5927

题意:

有一棵n个节点的有根树(1为树根),先定义重要节点x如下:1.x是重要节点。2.x的两个重要节点的lca。每次询问给出m个不重要节点(意味着其余点都是重要节点),问当前树中有重要节点的个数。

思路:

本题其实是求不重要节点中有多少节点可以变成重要节点。首先对树进行一次dfs,求出每个节点的父亲和儿子的个数,以及其深度,然后对于m个不重要节点,按深度从大到小排序,然后依次判断:对于当前点,如果它的儿子数量大于等于2,那么当前点一定是一个重要节点,如果儿子数量等于1,那么关系到当前点的祖先节点是不是重要节点,不处理,如果等于0,那么当前点肯定不是重要节点,同时其父亲节点的儿子数量减1

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
#include <cmath>
using namespace std;

const int N = 100100;
struct edge
{
    int to, next;
}g[N*2];
int cnt, head[N];
int son[N], par[N], d[N], dep[N], tmp[N];
bool vis[N];
int n, m, cas;
void add_edge(int v, int u)
{
    g[cnt].to = u, g[cnt].next = head[v], head[v] = cnt++;
}
void dfs(int v, int fa, int d)
{
    vis[v] = true, par[v] = fa, dep[v] = d;
    for(int i = head[v]; i != -1; i = g[i].next)
    {
        int u = g[i].to;
        if(! vis[u]) dfs(u, v, d + 1), son[v]++;
    }
}
bool cmp(int a, int b)
{
    return dep[a] > dep[b];
}
int main()
{
    int t;
    scanf("%d", &t);
    while(t--)
    {
        scanf("%d%d", &n, &m);
        cnt = 0;
        for(int i = 1; i <= n; i++) head[i] = -1, son[i] = 0, vis[i] = false;
        int v, u;
        for(int i = 1; i <= n - 1; i++)
        {
            scanf("%d%d", &v, &u);
            add_edge(v, u), add_edge(u, v);
        }
        dfs(1, -1, 1);
        printf("Case #%d:\n", ++cas);
        for(int i = 1; i <= m; i++)
        {
            int k;
            scanf("%d", &k);
            int ans = n - k;
            for(int j = 1; j <= k; j++) scanf("%d", &d[j]);
            for(int j = 1; j <= k; j++) tmp[d[j]] = son[d[j]];
            sort(d+1, d+1+k, cmp);
            for(int j = 1; j <= k; j++)
                if(tmp[d[j]] >= 2) ans++;
                else if(tmp[d[j]] == 0) tmp[par[d[j]]]--;
            printf("%d\n", ans);
        }
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值