2020牛客多校第三场 G - Operating on a Graph (并查集+list)

Operating on a Graph

题意:

给定一个无向图,有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("");
    }
}
posted @ 2020-07-20 09:40  UCPRER  阅读(145)  评论(0编辑  收藏  举报