Describe
多组测试数据
给你n个点的树输入n-1条边后,q次询问,每次询问给你一个未知点集,点集外面的点都是已知的重要点,定义两个重要点的LCA也是重要点,问你一共有多少重要点
Solution
用到了LCA的一个性质:同一个节点的不同子树中的任何节点的LCA为该节点本身。所以我们要记录一个节点的所有儿子节点数目,然后根据未知顶点,依据深度从高到低排序,对于非重要点进行删除,然后层层向上判断,如果节点的子节点的重要节点多于两个,那么该节点为重要节点;如果是一个进行保留;如果没有;则将清理该节点的父节点;
Code
#include <bits/stdc++.h>
using namespace std;
const int maxn = 2e5 + 10;
struct node{
int to,net;
}edge[maxn];
int id[maxn],cnt;
int fa[maxn],son[maxn],deep[maxn];
int n;
bool cmp(int u,int v){
return deep[u] > deep[v];
}
void add(int from,int to){
edge[cnt].to = to;
edge[cnt].net = id[from];
id[from] = cnt++;
}
void init(){
memset(id,-1,sizeof(id));
cnt = 0;
}
//更新维护了子节点的个数,节点的深度,和父节点
void dfs(int now,int dep){
son[now] = 0;
deep[now] = dep;
for(int i = id[now];~i;i = edge[i].net){
int to = edge[i].to;
if(to != fa[now]){
fa[to] = now;
son[now]++;
dfs(to,dep+1);
}
}
}
int p[maxn],op[maxn];
int main()
{
int t,n,q;
scanf("%d",&t);
for(int cas = 1;cas <= t;++cas){
scanf("%d%d",&n,&q);
init();
int u,v;
for(int i = 1;i <= n - 1;++i){
scanf("%d%d",&u,&v);
add(u,v);
add(v,u);
}
fa[1] = -1;
dfs(1,1);
printf("Case #%d:\n",cas);
int k;
while(q--){
scanf("%d",&k);
//给出一个点集合
for(int i = 1;i <= k;++i){
scanf("%d",&p[i]);
//对于每个点,可提供支持的点的数量记录
op[p[i]] = son[p[i]];
}
//根据深度排序已知点集,由下到上
sort(p+1,p+k+1,cmp);
int ans = n - k;//一开始有n-k个重要的
for(int i = 1;i <= k;++i){//对于k个已知点
if(op[p[i]] >= 2)ans++;//如果他的子树中仍然存在两个以上的重要点那么他也为重要点
else if(op[p[i]] == 0)op[fa[p[i]]]--;//如果不存在点,那么就不是重要的点
}
printf("%d\n",ans);
}
}
return 0;
}