参考博客:https://blog.csdn.net/fsss_7/article/details/52745518
题意:
给出一个树,和一个点集,点集外的点为“重要的点”,如果某个点集内的点是两个“重要的点的LCA”,那么它也是“重要的点”,其他点为“非重要的点”。
求出重要的点的个数。
分析:
其实对于每一个询问我们只要判断哪些不重要的点是能进入集合的,那么对于一个不重要的点x,怎样才能进入集合呢?我们先dfs对于所有的点求出fa[x]和son[x]。当询问是,我们先初始确定每个不重要的点的son'[x],如果一个点要进入集合那么显然要求son'[x]>=2,这样才会有两个重要的点的最近公共祖先是x。那么对于一个点x的son'又会受什么影响呢?当一个点y满足son'[y]==0&&y是不重要的点那么son'[fa[y]]--。这样我们就能维护我们需要的信息了。O(mlogm)
#include<bits/stdc++.h>
using namespace std;
const int maxn=2e5+10;
int dep[maxn];
int fa[maxn];
int son[maxn];
int son_[maxn];
int d[maxn];
int head[maxn];
int tot;
struct node{
int to;
int next;
}g[maxn];
void addedge(int u,int v){
g[tot].to=v;
g[tot].next=head[u];
head[u]=tot++;
}
void dfs(int v,int pre){
fa[v]=pre;
dep[v]=dep[pre]+1;
son[v]=0;
for(int i=head[v];~i;i=g[i].next){
if(g[i].to!=pre){
dfs(g[i].to,v);
son[v]++;
}
}
}
bool cmp(int a,int b){
return dep[a]>dep[b];
}
void init(){
memset(head,-1,sizeof head);
tot=0;
dep[0]=0;
}
int main(){
int T;
scanf("%d",&T);
for(int cs=1;cs<=T;cs++){
init();
int n,q;
scanf("%d%d",&n,&q);
for(int i=0;i<n-1;i++){
int u,v;
scanf("%d%d",&u,&v);
addedge(u,v);
addedge(v,u);
}
dfs(1,0);
printf("Case #%d:\n",cs);
while(q--){
int m;
scanf("%d",&m);
int ans=n-m;
for(int i=0;i<m;i++){
scanf("%d",&d[i]);
}
sort(d,d+m,cmp);
for(int i=0;i<m;i++) son_[d[i]]=son[d[i]];
for(int i=0;i<m;i++){
if(son_[d[i]]>=2) ans++;
else if(son_[d[i]]==0) son_[fa[d[i]]]--;
}
printf("%d\n",ans);
}
}
return 0;
}