Describe
输入n,m,给你一个树有n个节点m-1条边,输入m-1个整数,第i个整数为节点i+1的父节点;然后输入m条边,表示一个图,输入q表示有q次询问:每次询问输入k和k个整数,k个整数表示树上节点编号,树上节点编号对应图中边节点编号,集合S由输入的树上节点编号以及所有祖先节点构成,对于每一个询问,输出由集合S引入边集合后,途中有几个联通块
https://blog.csdn.net/snowy_smile/article/details/52757816
Solution
依据dfs初始化并查集f[i][n],从上到下层层更新,表示引入i节点后(前面已经初始化好i的所有祖先节点了)的并查集联通情况,然后依据输入的节点更新一个并查集,最后判断有几个老大就可以了
Code
#include <bits/stdc++.h>
using namespace std;
const int maxn = 505,maxm = 1e4 + 10;
int n,m;
vector<int> mp[maxm];//树有m个点
pair<int,int> G[maxm];
int f[maxm][maxn];//对于树中的每一个节点都要遍历得到对应图中节点的状态
inline int Find(int f[],int x){
return f[x] == x ? x : f[x] = Find(f,f[x]);
//等号写成了 ==
}
inline void join(int f[],int a,int b){
a = Find(f,a);
b = Find(f,b);
f[b] = a;
}
void dfs(int x,int fa){
//状态叠加传递
memcpy(f[x],f[fa],sizeof(f[fa]));
//引入当前节点后状态改变
join(f[x],G[x].first,G[x].second);
//依据树节点向下遍历
for(auto to : mp[x]){
dfs(to,x);
}
}
int d[maxn];
void solve(int cas){
printf("Case #%d:\n",cas);
int q;
scanf("%d",&q);
while(q--){
int k,x;
memcpy(d,f[0],sizeof(f[0]));
scanf("%d",&k);
while(k--){
scanf("%d",&x);
for(int i = 1;i <= n;++i){
join(d,i,Find(f[x],i));
}
}
int ans = 0;
for(int i = 1;i <= n;++i)
if(Find(d,i) == i) ++ans;
printf("%d\n",ans);
}
}
int main()
{
int t;
scanf("%d",&t);
for(int cas = 1;cas <= t;++cas){
scanf("%d%d",&n,&m);
for(int i = 1;i <= m;++i)mp[i].clear();
//树 m个节点
int tmp;
for(int i = 2;i <= m;++i){
scanf("%d",&tmp);
mp[tmp].push_back(i);
}
//图中的m条边
for(int i = 1;i <= m;++i){
scanf("%d%d",&G[i].first,&G[i].second);
}
for(int i = 1;i <= n;++i)f[0][i] = i;
dfs(1,0);
solve(cas);
}
return 0;
}