题目链接:
F - Auxiliary Set
学习网址:https://blog.csdn.net/yiqzq/article/details/81952369
题目大意一棵节点数为n的有根数,根节点为1,一开始所有的点都是重点,接下来有q次询问,每次询问把m个点变为轻点,问你树中还有多少个重点。
重点应该满足的条件为:
1.它本身是重点。
2.它为两个重点的最近公共祖先。
每次询问之后在下次询问前,所有的点都恢复为重点。
具体思路:对于每个点保存他的深度。因为每次输入的数不重要的点,首先对于这些不重要的点按照深度从大到小进行排序, 然后先去处理深度大的。当处理深度大的时候,可以保证这个节点下面是没有不重要的点的,也就是这个节点的下面全都是重要的节点,那么这个点也肯定是符合的。然后往上更新的时候,如果当前的不重要节点下面没有点了,那么这个不重要节点的父亲往下的儿子数就应该减去1,也就是说这个分支下不存在重要的节点了。
AC代码:
#include<bits/stdc++.h>
using namespace std;
# define ll long long
const int maxn = 2e5+;
int n,m;
vector<int>edge[maxn];
void addedge(int fr,int to)
{
edge[fr].push_back(to);
}
int sto[maxn];
int depth[maxn];
int son1[maxn],son2[maxn];
int father[maxn];
void init()
{
for(int i=; i<=n; i++)
{
edge[i].clear();
son1[i]=;
}
}
void dfs(int cur,int fa,int dep)
{
father[cur]=fa;
depth[cur]=dep+;
for(int i=; i<edge[cur].size(); i++)
{
int to=edge[cur][i];
if(to==fa)
continue;
son1[cur]++;
dfs(to,cur,dep+);
}
}
bool cmp(int t1,int t2)
{
return depth[t1]>depth[t2];
}
int main()
{
int T;
int Case=;
scanf("%d",&T);
while(T--)
{
int st,ed;
scanf("%d %d",&n,&m);
init();
for(int i=; i<n; i++)
{
scanf("%d %d",&st,&ed);
addedge(st,ed);
addedge(ed,st);
}
printf("Case #%d:\n",++Case);
dfs(,,);
while(m--)
{
int sz,ans;
scanf("%d",&sz);
ans=n-sz;
for(int i=; i<=sz; i++)
{
scanf("%d",&sto[i]);
son2[sto[i]]=son1[sto[i]];
}
sort(sto+,sto+sz+,cmp);
for(int i=; i<=sz; i++)
{
if(son2[sto[i]]>=)
ans++;
if(son2[sto[i]]==)
son2[father[sto[i]]]--;
}
printf("%d\n",ans);
}
}
return ;
}