题意:
给定一个无向图,有n个点,点i初始时属于集合i。给出q个操作,每次操作针对集合oi,将与集合oi相邻的集合全部加入集合oi中(若集合oi已经不存在了就无事发生)。在q个操作结束之后,问每个点属于的集合。
解法:
显然应该用并查集维护每个点属于哪个集合。问题在于进行操作时,如何确定和集合oi相邻的集合?因此每个集合必须要有一个容器存储其中的点,这个容器还要能够快速合并。因此选用List作为集合的容器。
在一次操作中,有一些点被展开,和它相邻的点属于的集合都被归入oi,可以发现,这个点只需要被扩展一次就够了,因为之后它相邻的点一定是和它属于同一个集合的。
所以在一次操作中,只需要将list中原有的所有点都扩展并弹出,并加入相邻集合的点,同时维护并查集关系。
时间复杂度为O(nlogn+m)大概
代码:
#include <bits/stdc++.h>
using namespace std;
const int maxn=8e5+5;
vector<int>E[maxn];
list<int>ls[maxn];
int fa[maxn];
int find(int x){
return x==fa[x]?x:fa[x]=find(fa[x]);
}
int unite(int u,int v){
int fu=find(u);
int fv=find(v);
if(fu!=fv){
fa[fu]=fv;
ls[fv].splice(ls[fv].end(),ls[fu]);
}
}
void bfs(int oi){
if(find(oi)!=oi){//不存在该集合
return;
}
int size=ls[oi].size();//只要展开集合中原有的点,防止展开新的点
for(int i=0;i<size;i++){
int curnode=ls[oi].front();
for(auto v:E[curnode]){
unite(v,oi);//将v加入oi
}
ls[oi].pop_front();
}
}
void init(int n){
for(int i=0;i<n;i++){
fa[i]=i;
E[i].clear();
ls[i].clear();
ls[i].push_back(i);//初始化集合元素为自己
}
}
int main () {
int T;
scanf("%d",&T);
while(T--){
int n,m;
scanf("%d%d",&n,&m);
init(n+3);
for(int i=1;i<=m;i++){
int u,v;
scanf("%d%d",&u,&v);
E[u].push_back(v);
E[v].push_back(u);
}
int q;
scanf("%d",&q);
while(q--){
int oi;
scanf("%d",&oi);
bfs(oi);
}
for(int i=0;i<n;i++){
printf("%d",find(i));
if(i<n){
printf(" ");
}
}
puts("");
}
}