1.题目链接。题目大意就是给了一棵树,这棵树上的节点有两种类别,一种是重要的点,一种是不重要的点。然后现在有一个集合,这个集合里面的节点满足两条性质中的任意一条即可:(1)这个节点是一个重要的节点.(2)这个节点不是一个重要的节点,但是这个节点是两个重要节点的LCA。然后是m组询问,每组询问首先给出一个数t,然后给出t个点,这t个点是不重要的节点,对于每组询问,输出一下这个集合的大小(也就是元素的数量)。
2分析一下可以知道,我们这个问题的主要矛盾就是在解决那些不重要的点中,有多少个是两个重要点的LCA。然后把重要的点加上去就是答案了。对于LCA我们知道,一个节点是他不同子树的LCA。然后我们只需要知道这个节点中儿子中重要节点的数量,如果这个数量大于等于2.那么这个节点就会被加入集合中,如果这个节点没有儿子,那么他对他的父亲贡献是0,所以父亲的儿子节点数减一。然后做法就比较明显了,我们一遍dfs,记录这样三个信息:当前节点的层数,节点儿子个数,节点的父亲。然后按照最底层从下向上更新即可。复杂度m*logm.
using namespace std;
const int N = 1e5 + 100;
vector<int>vec[N];
int dep[N];
int fa[N];
int cnt[N];
int vis[N];
int son[N];
struct node
{
int x, d;
}no[N];
void dfs(int x, int y, int depth)
{
fa[x] = y;
dep[x] = depth;
vis[x] = 1;
for (int i = 0; i < vec[x].size(); i++)
{
if (vis[vec[x][i]])continue;
cnt[x]++;
dfs(vec[x][i], x, depth + 1);
}
}
int cmp(node a, node b)
{
return a.d > b.d;
}
int main()
{
int n, m;
int T;
scanf("%d", &T);
for (int ca = 1; ca <= T; ca++)
{
scanf("%d%d", &n, &m);
memset(vis, 0, sizeof(vis));
memset(cnt, 0, sizeof(cnt));
for (int i = 0; i <= n; i++)vec[i].clear();
for (int i = 1; i <n; i++)
{
int u, v;
scanf("%d%d", &u, &v);
vec[u].push_back(v);
vec[v].push_back(u);
}
dfs(1, 0, 1);
printf("Case #%d:\n", ca);
while (m--)
{
int t;
scanf("%d", &t);
int ans = n - t;
for (int j = 0; j < t; j++)
{
int x;
scanf("%d", &x);
no[j].x = x;
no[j].d = dep[x];
son[x] = cnt[x];
}
sort(no, no + t, cmp);
for (int k = 0; k < t; k++)
{
if (son[no[k].x] >= 2)ans++;
else if (son[no[k].x] == 0)son[fa[no[k].x]]--;
}
printf("%d\n", ans);
}
}
return 0;
}