2020牛客暑期多校训练营(第三场)G-Operating on a Graph(并查集)
题目链接
题意就是给定初始的图,每个点初始都各自属于一个组。然后每一次操作给定一个数字\(k\),这次操作会将与组\(k\)中的任意一点相邻的点的对应组合并到组\(k\)中。最后求每个点的组编号。
既然是分组,那么利用并查集应该是很明确的,注意到一旦合并之后那么这两个组的关系就永远相同了,就算是变化的话也是一起变化。那么具体操作起来的话每次组合并只要把所有相关点缩到祖先节点上,然后通过祖先节点与其余点根据原来的边情况进行重连就可以了。
#include <bits/stdc++.h>
using namespace std;
const int maxn=8e5+10;
int pre[maxn];
vector<int> edg[maxn];
inline int find(int x){return x==pre[x]?x:pre[x]=find(pre[x]);}
void merge(int n){
vector<int> tmp=edg[n];
edg[n].clear();
for(int i : tmp){
int k=find(i);
if(k!=n){
pre[k]=n;
if(edg[n].size()<edg[k].size())swap(edg[n],edg[k]);
for(auto v:edg[k]){
edg[n].push_back(v);
}
edg[k].clear();
}
}
}
int main(){
int t;cin>>t;
while(t--){
int n,m;
cin>>n>>m;
for(int i=0;i<n;i++){
pre[i]=i;
edg[i].clear();
}
for(int i=0,u,v;i<m;i++){
cin>>u>>v;
edg[u].push_back(v);
edg[v].push_back(u);
}
int q;
cin>>q;
while(q--){
int k;
cin>>k;
if(find(k)!=k)continue;
merge(k);
}
for(int i=0;i<n;i++)cout<<find(i)<<" ";
cout<<"\n";
}
return 0;
}