题目链接:Problem - 5923
题面:
题意:
题意挺难理解的,有一张有n个点m条边的图,给定每个边的节点的父亲节点,现在这m条边组成了一棵根节点为1的魔术树(树的结点为边的id),然后给定这些边的节点,后面q次查询,查询这棵树的节点和所有父亲节点对应的边连接起来,一共有几个组件数量
思路:因为最后查询的集合内的内一个节点和所有父亲节点的联通图的组件数量,但是查询数量很大,我们不能反向查询父亲节点的方法去合并,但是我们可以通过预处理的方法,让儿子节点的并查集继承父亲节点, 然后通过把集合内点合并的方法就可以合并所需要的所有节点,然后遍历一遍,找几个组件数量即可
#include <bits/stdc++.h>
using namespace std;
#define endl "\n"
#define N 505
#define M 10005
int cnt = 0;
int to[M];
int h[M];
int nex[M];
int u[M], v[M];
int s[M][N];
int arr[300005];
void add(int x, int y){
to[cnt] = y;
nex[cnt] = h[x];
h[x] = cnt++;
}
int findx(int x, int y){
if(s[x][y] == y){
return y;
}
return s[x][y] = findx(x,s[x][y]);
}
void merge(int m, int x, int y){
int fx = findx(m, x);
int fy = findx(m, y);
if(fx != fy){
s[m][fy] = fx;
}
}
void dfs(int x, int f){
memcpy(s[x], s[f], sizeof(s[x]));//儿子节点继承父亲节点的状态
merge(x, u[x], v[x]);
for(int i = h[x]; i != -1; i = nex[i]){
int too = to[i];
dfs(too, x);
}
}
int main(){
ios::sync_with_stdio(false);
int t;
cin >> t;
int ans = 1;
while(t--){
cnt = 0;
int n, m;
cin >> n >> m;
for(int i = 1; i <= m; i++){
h[i] = -1;
}
for(int i = 2; i <= m; i++){
int a;
cin >> a;
add(a, i);
}
for(int i = 1; i <= m; i++){
cin >> u[i] >> v[i];
}
for(int i = 1; i <= n; i++) {
s[1][i] = i;
}
dfs(1, 1);
int q;
cin >> q;
cout << "Case #" << ans++ << ":" << endl;
while(q--){
int k;
cin >> k;
for(int i = 0; i < k; i++){
cin >> arr[i];
}
memcpy(s[m + 1], s[arr[0]], sizeof(s[m + 1]));
//把集合内剩下的点进行合并
for(int i = 1; i < k; i++){
for(int j = 1; j <= n; j++){
merge(m + 1, findx(arr[i], j), j);
}
}
int num = 0;
//查询有几个联通路
for(int i = 1; i <= n; i++){
if(findx(m + 1, i) == i){
num++;
}
}
cout << num << endl;
}
}
return 0;
}