题意:给定一棵以1为根的n个节点的树,多个询问,每次询问给出一个集合,集合内的点表示为不重要的点(不在集合内的点就是重要的点),求给定这个集合后有多少点能进入另一个集合,点x进入另一个集合的要求:1:重要的点。2:有两个重要的点的最近公共祖先为x。
分析:其实对于每一个询问我们只要判断哪些不重要的点是能进入集合的,那么对于一个不重要的点x,怎样才能进入集合呢?我们先dfs对于所有的点求出fa[x]和son[x]。当询问是,我们先初始确定每个不重要的点的son'[x],如果一个点要进入集合那么显然要求son'[x]>=2,这样才会有两个重要的点的最近公共祖先是x。那么对于一个点x的son'又会受什么影响呢?当一个点y满足son'[y]==0&&y是不重要的点那么son'[fa[y]]--。这样我们就能维护我们需要的信息了。
代码如下:
#include <bits/stdc++.h>
using namespace std;
#define LL long long
#define INF 1E4 * 1E9
#define pi acos(-1)
#define endl '\n'
#define me(x) memset(x,0,sizeof(x));
const int maxn=1e3+5;
const int maxx=2e5+5;
vector<int>G[maxx];
int fa[maxx],son[maxx],deep[maxx],now[maxx],a[maxx];
int n,m,q,u,v;
int cas=1;
void dfs(int u,int f,int dep)
{
deep[u]=dep;
for(int i=0;i<G[u].size();i++)
{
if(G[u][i]==f)continue;
son[u]++;
dfs(G[u][i],u,dep+1);
fa[G[u][i]]=u;
}
}
int cmp(int a,int b)
{
return deep[a]>deep[b];
}
void hy()
{
for(int i=1;i<=m;i++)
{
int father=fa[a[i]];
if(now[father])
{
son[father]+=now[father];
now[father]=0;
}
}
}
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
scanf("%d %d",&n,&q);
me(fa);me(son);me(deep);me(now);me(a);
for(int i=0;i<=n;i++) G[i].clear();
for(int i=1;i<n;i++)
{
scanf("%d %d",&u,&v);
G[u].push_back(v);
G[v].push_back(u);
}
dfs(1,0,1);
printf("Case #%d:\n",cas++);
/*for(int i=1;i<15;i++)
{
cout<<endl<<i<<"______i________"<<endl;
cout<<deep[i]<<"_____deep"<<endl;
cout<<fa[i]<<"__fa"<<endl;
cout<<son[i]<<"__________si"<<endl;
}*/
while(q--)
{
scanf("%d",&m);
int ans=0;
for(int i=1;i<=m;i++) cin>>a[i];
sort(a+1,a+m+1,cmp);
//memcpy(now,son,n*sizeof(int));
//for(int i=1;i<=n;i++) cout<<now[i]<<"________"<<endl;
for(int i=1;i<=m;i++)
{
int father=fa[a[i]];
if(son[a[i]]>=2) ans++;
if(son[a[i]]==0)
{
son[father]--;
now[father]++;
}
}
hy();
printf("%d\n",n-m+ans);
}
}
}