题目:
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;
}