地址传送门
题意:分别给你一个有根树还有一个图,有根树得每个节点代表图的一条边。每次询问给你一个集合,把集合里所有的点以及所有点的祖先节点代表得边连起来。问连接后的图中有多少个连通分量
思路:集合那不用说并查集了,这道题主要考虑如何只把他和他的祖父节点连接起来,于是把数组开成二维,dfs整棵树,每一节点建一个并查集数组,由于这道题是要把他的祖父节点也在集合,于是他祖父节点在一个集合的数他也会在一个集合,由此直接复制他父节点的并查集数组,再把自己节点的点放到一个集合中
#include<bits/stdc++.h>
using namespace std;
#define rd(a) scanf("%d",&a)
int ss[10009][600];
int find(int x,int m)
{
return x==ss[m][x]?x:ss[m][x]=find(ss[m][x],m);
}
struct node
{
int u,v;
}ar[10009];
struct tre
{
int v,next;
}a[10009];
int head[10009];
int o=0;
int n,m;
void intt()
{
memset(head,-1,sizeof(head));
o=0;
for(int i=0;i<=m;i++)
for(int j=0;j<=n;j++)
ss[i][j]=j;
}
inline void add(int u,int v)
{
a[o].v=v;
a[o].next=head[u];
head[u]=o++;
}
void dfs(int u)
{
for(int i=head[u];~i;i=a[i].next)
{
int v=a[i].v;
for(int j=0;j<=n;j++)
{
ss[v][j]=ss[u][j];
}
int x=find(ar[v].u,v);
int y=find(ar[v].v,v);
if(x!=y)
{
ss[v][x]=y;
}
dfs(v);
}
}
int main()
{
// freopen("E://in.txt","r",stdin);
int t;
rd(t);
// cout<<t;
int tt=1;
while(t--)
{
printf("Case #%d:\n",tt++);
rd(n),rd(m);
intt();
for(int i=2;i<=m;i++)
{
int k;
rd(k);
add(k,i);
}
for(int i=1;i<=m;i++)
{
rd(ar[i].u),rd(ar[i].v);
}
add(0,1);
dfs(0);
int tt;
rd(tt);
while(tt--)
{
int nn;
rd(nn);
//for(int i=0;i<=n;i++)
//ss[m+1][i]=i;
int su[10009];
for(int i=0;i<nn;i++) rd(su[i]);
for(int i=0;i<=n;i++) ss[m+1][i]=ss[su[0]][i];
for(int i=1;i<nn;i++)
{
for(int j=1;j<=n;j++)
{
int r=find(j,su[i]);
int x=find(r,m+1);
int y=find(j,m+1);
if(x!=y)
ss[m+1][x]=y;
}
}
int ans=0;
int vis[600];
memset(vis,0,sizeof(vis));
for(int i=1;i<=n;i++)
{ int x=find(i,m+1);
if(!vis[x])
{
vis[x]=1;
ans++;
}
}
printf("%d\n",ans);
}
}
return 0;
}