2020牛客多校第三場 G - Operating on a Graph (並查集+list)
阿新 • • 發佈:2020-07-20
題意:
給定一個無向圖,有n個點,點i初始時屬於集合i。給出q個操作,每次操作針對集合oi,將與集合oi相鄰的集合全部加入集合oi中(若集合oi已經不存在了就無事發生)。在q個操作結束之後,問每個點屬於的集合。
解法:
顯然應該用並查集維護每個點屬於哪個集合。問題在於進行操作時,如何確定和集合oi相鄰的集合?因此每個集合必須要有一個容器儲存其中的點,這個容器還要能夠快速合併。因此選用List作為集合的容器。
在一次操作中,有一些點被展開,和它相鄰的點屬於的集合都被歸入oi,可以發現,這個點只需要被擴充套件一次就夠了,因為之後它相鄰的點一定是和它屬於同一個集合的。
所以在一次操作中,只需要將list中原有的所有點都擴充套件並彈出,並加入相鄰集合的點,同時維護並查集關係。
程式碼:
#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(""); } }