1. 程式人生 > 實用技巧 >2020牛客暑期多校第三場G-Operating on a Graph(並查集+list)

2020牛客暑期多校第三場G-Operating on a Graph(並查集+list)

題目大意:給你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){return
x==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; }