2020牛客暑期多校第三場G-Operating on a Graph(並查集+list)
阿新 • • 發佈:2020-07-21
題目大意:給你n個點,m條邊,現在有q次操作,每次一個x,表示將與x所連的所有點歸併到x團裡面,問最後每個點屬於哪個團。
輸入
5 4 3 0 1 1 2 2 3 4 0 1 3 0 4 3 0 1 1 2 2 3 2 0 2 4 3 0 1 1 2 2 3 2 0 3 4 1 1 3 1 2 5 5 0 1 0 2 1 2 1 3 3 4 3 4 4 0
輸出
0 0 0 0 2 2 2 2 0 0 3 3 0 1 2 3 0 0 0 0 0
看見團和合並之類的,很容易聯想到並查集,每次合併的時候我們將每個點的祖宗設定為x就好了(注意,如果x的祖宗不是自己的話就說明它已經被合併了,這個x團中就是空的,我們直接跳過)。接下來就是將兩個祖宗團的所有點合併了,我們需要用一個數據結構來實現這種操作,而list應該是一個比較理想的容器,它的splice方法是比較高效的
以下是AC程式碼:
#include <bits/stdc++.h> using namespace std; const int mac=1e6+10; struct Edge { int to,next; }eg[mac<<1]; int head[mac],father[mac],num=0; list<int>group[mac]; bool vis[mac]; void add(int u,int v) { eg[++num]=Edge{v,head[u]}; head[u]=num; } int find(int x){returnx==father[x]?x:father[x]=find(father[x]);} void unions(int u,int v) { int fu=find(u),fv=find(v); if (fu!=fv){ father[fu]=fv; group[fv].splice(group[fv].end(),group[fu]); } } int main(int argc, char const *argv[]) { int t,n,m; scanf ("%d",&t); while (t--){ scanf ("%d%d",&n,&m); num=0; for (int i=0; i<n+10; i++){ head[i]=-1;father[i]=i; vis[i]=0; group[i].clear(); group[i].push_back(i); } for (int i=1; i<=m; i++){ int u,v; scanf ("%d%d",&u,&v); add(u,v);add(v,u); } int q; scanf ("%d",&q); while (q--){ int x; scanf ("%d",&x); int gkd=find(x); if (gkd!=x) continue; int len=group[x].size(); for (int i=0; i<len; i++){//!!注意這裡不能用size。 int u=group[x].front(); for (int j=head[u]; j!=-1; j=eg[j].next){ int v=eg[j].to; unions(v,x); if (vis[v]) continue; group[x].push_back(v); vis[v]=1; } group[x].pop_front(); } } for (int i=0; i<n; i++) printf("%d ",find(i)); printf("\n"); } return 0; }