並查集求聯通塊個數【洛谷P1197】
阿新 • • 發佈:2018-11-21
傳送門:https://www.luogu.org/problemnew/show/P1197
並查集求聯通塊個數。
這個題是每摧毀一個點求一次聯通塊的個數,並查集不容易維護這種摧毀更新的,相反的,並查集可以很好的維護每安裝一個點更新一次這種問題。摧毀和安裝是對立的,既然按順序摧毀,我們就逆序安裝。
首先要算出沒安裝前(也就是摧毀之後)的聯通塊的個數。怎麼求呢,我們在記錄圖的邊的時候記錄一下起點和終點,然後我們for一遍邊集,如果起點和終點都沒有被摧毀,我們就將他們放進並查集。
然後首先計算一下最終答案。並查集每一個find(i)==i都是一個聯通塊。
然後我們開始安裝點。
每安裝一個點,先將tot++,然後遍歷它的邊,如果它的相鄰點沒有被摧毀(或者建好了),也就是相鄰點還在,並且他們不是一個集合,我們就讓tot--,並且將該點放進並查集。(為什麼要tot--,大家自己動手畫個簡單的圖就能明白了。)
大致思路就是這樣啦!
下面是大家喜聞樂見的程式碼(話說我覺得自己的碼風還不錯哎)
#include <bits/stdc++.h> using namespace std; typedef pair<int,int> P; const int maxn = 5e5+7; int p[maxn],r[maxn]; int b[maxn]; int cur[maxn]; int ans[maxn]; vector<int> G[maxn]; vector<pair<int,int> >e; void init() { for(int i=0;i<maxn;i++) { p[i] = i; r[i] = 0; } } int find(int x) { if(x==p[x]) return x; return p[x] = find(p[x]); } void unite(int x,int y) { x = find(x); y = find(y); if(x==y) return; if(r[x]<r[y]) { p[x] = y; } else { p[y] = x; if(r[x]==r[y]) { r[x]++; } } } bool same(int x,int y) { return find(x)==find(y); } int main() { ios::sync_with_stdio(false); cin.tie(0); init(); int n,m; cin>>n>>m; for(int i=0;i<m;i++) { int x,y; cin>>x>>y; G[x].push_back(y); G[y].push_back(x); e.push_back(P(x,y)); } int k; cin>>k; for(int i=0;i<k;i++) { cin>>b[i]; cur[b[i]] = 1; } for(int i=0;i<e.size();i++) { int x = e[i].first; int y = e[i].second; if(!cur[x] && !cur[y]) { unite(x,y); } } int tot = 0; for(int i=0;i<n;i++) { if(find(i)==i && !cur[i]) { tot++; } } for(int i=k-1;i>=0;i--) { cur[b[i]] = 0; ans[i] = tot; tot++; if(G[b[i]].size()) { for(int j=0;j<G[b[i]].size();j++) { if(!cur[G[b[i]][j]]) { if(!same(b[i],G[b[i]][j])) { tot--; unite(b[i],G[b[i]][j]); } } } } } cout<<tot<<endl; for(int i=0;i<k;i++) { cout<<ans[i]<<endl; } return 0; }