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

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

題目連結

題意就是給定初始的圖,每個點初始都各自屬於一個組。然後每一次操作給定一個數字\(k\),這次操作會將與組\(k\)中的任意一點相鄰的點的對應組合併到組\(k\)中。最後求每個點的組編號。
既然是分組,那麼利用並查集應該是很明確的,注意到一旦合併之後那麼這兩個組的關係就永遠相同了,就算是變化的話也是一起變化。那麼具體操作起來的話每次組合並只要把所有相關點縮到祖先節點上,然後通過祖先節點與其餘點根據原來的邊情況進行重連就可以了。

#include <bits/stdc++.h>
using namespace std;
const int maxn=8e5+10;
int pre[maxn];
vector<int> edg[maxn];
inline int find(int x){return x==pre[x]?x:pre[x]=find(pre[x]);}
void merge(int n){
    vector<int> tmp=edg[n];
    edg[n].clear();
    for(int i : tmp){
        int k=find(i);
        if(k!=n){
            pre[k]=n;
            if(edg[n].size()<edg[k].size())swap(edg[n],edg[k]);
            for(auto v:edg[k]){
                edg[n].push_back(v);
            }
            edg[k].clear();
        }
    }
}
int main(){
    int t;cin>>t;
    while(t--){
        int n,m;
        cin>>n>>m;
        for(int i=0;i<n;i++){
            pre[i]=i;
            edg[i].clear();
        }
        for(int i=0,u,v;i<m;i++){
            cin>>u>>v;
            edg[u].push_back(v);
            edg[v].push_back(u);
        }
        int q;
        cin>>q;
        while(q--){
            int k;
            cin>>k;
            if(find(k)!=k)continue;
            merge(k);
        }
        for(int i=0;i<n;i++)cout<<find(i)<<" ";
        cout<<"\n";
    }
    return 0;
}