雙連通分量
阿新 • • 發佈:2022-03-10
前置知識
定義
對於無向圖中的兩點 \(u,v\),若無論刪去哪條邊都不能使得它們不連通,那麼我們稱 \(u,v\) 邊雙連通。
對於無向圖中的兩點 \(u,v\),若無論刪去哪個點都不能使得它們不連通,那麼我們稱 \(u,v\) 點雙連通。
其中邊雙連通具有傳遞性,點雙連通不具有傳遞性。
邊雙連通
去掉橋,分成的個連通塊就是一個個邊雙連通分量。
直接Tarjan求割點,然後刪掉橋就可以得到邊雙連通分量。
不過一般來說我們不直接刪掉橋,而是以每個點開始走dfs,不走割邊,然後直接染色即可。
#include<bits/stdc++.h> #define ll long long #define db double #define filein(a) freopen(#a".in","r",stdin) #define fileot(a) freopen(#a".out","w",stdout) #define sky fflush(stdout); #define Better_IO 1 namespace IO{ inline bool blank(const char &c){ return c==' ' or c=='\n' or c=='\t' or c=='\r' or c==EOF; } #if Better_IO==true char buf[(1<<20)+3],*p1(buf),*p2(buf); char buf2[(1<<20)+3],*p3(buf2); const int lim=1<<20; inline char gc(){ if(p1==p2) p2=(p1=buf)+fread(buf,1,lim,stdin); return p1==p2?EOF:*p1++; } #define pc putchar #else #define gc getchar #define pc putchar #endif inline void gs(char *s){ char ch=gc(); while(blank(ch) ) {ch=gc();} while(!blank(ch) ) {*s++=ch;ch=gc();} *s=0; } inline void gs(std::string &s){ char ch=gc();s+='#'; while(blank(ch) ) {ch=gc();} while(!blank(ch) ) {s+=ch;ch=gc();} } inline void ps(char *s){ while(*s!=0) pc(*s++); } inline void ps(const std::string &s){ for(auto it:s) if(it!='#') pc(it); } template<class T> inline void read(T &s){ s=0;char ch=gc();bool f=0; while(ch<'0'||'9'<ch) {if(ch=='-') f=1;ch=gc();} while('0'<=ch&&ch<='9') {s=s*10+(ch^48);ch=gc();} if(ch=='.'){ db p=0.1;ch=gc(); while('0'<=ch&&ch<='9') {s=s+p*(ch^48);ch=gc();} } s=f?-s:s; } template<class T,class ...A> inline void read(T &s,A &...a){ read(s);read(a...); } }; using IO::read; using IO::gs; using IO::ps; const int M=6e5+3; const int N=5e4+3; int n,m; int Etot=-1; int head[M],nxt[M]; struct Edge{ int u,v; bool cut; }to[M]; inline void join(int u,int v){ nxt[++Etot]=head[u]; head[u]=Etot; to[Etot]={u,v,0}; } int idx; int dfn[N],low[N]; void dfs1(int u,int f){ dfn[u]=low[u]=++idx; for(int i=head[u];~i;i=nxt[i]){ int v=to[i].v; if(!dfn[v]){ dfs1(v,u); low[u]=std::min(low[u],low[v]); if(dfn[u]<low[v]){ to[i].cut=to[i^1].cut=1; } }else if(v!=f){ low[u]=std::min(low[u],dfn[v]); } } } int Ctot,col[N]; void dfs2(int u,int f){ col[u]=Ctot; for(int i=head[u];~i;i=nxt[i]){ int v=to[i].v; if(v==f or to[i].cut) continue; if(col[v]) continue; dfs2(v,u); } } int main(){ filein(a);fileot(a); read(n,m); memset(head,-1,sizeof(head) ); for(int i=1;i<=m;++i){ int u,v; read(u,v); join(u,v);join(v,u); } idx=0; for(int i=1;i<=n;++i){ if(!dfn[i]){ dfs1(i,i); } } for(int i=1;i<=n;++i){ if(!col[i]){ ++Ctot; dfs2(i,i); } } printf("%d\n",Ctot); return 0; }
點雙連通
不難發現,一個點雙連通分量是被一個割點所分開,並且這個割點也在點雙連通分量內。我們可以像維護強連通分量一樣,將點入棧。我們在檢驗一個點是否為割點時,記當前點為 \(u\),目標點為 \(v\) ,此時 \(low[v]<=dfn[u]\) , 就直接瘋狂彈出棧,直到彈出目標點 \(v\),把彈出的點全部存入一個點雙連通分量,再把 \(u\) 加入即可。
#include<bits/stdc++.h> #define ll long long #define db double #define filein(a) freopen(#a".in","r",stdin) #define fileot(a) freopen(#a".out","w",stdout) #define sky fflush(stdout); #define Better_IO 1 namespace IO{ inline bool blank(const char &c){ return c==' ' or c=='\n' or c=='\t' or c=='\r' or c==EOF; } #if Better_IO==true char buf[(1<<20)+3],*p1(buf),*p2(buf); char buf2[(1<<20)+3],*p3(buf2); const int lim=1<<20; inline char gc(){ if(p1==p2) p2=(p1=buf)+fread(buf,1,lim,stdin); return p1==p2?EOF:*p1++; } #define pc putchar #else #define gc getchar #define pc putchar #endif inline void gs(char *s){ char ch=gc(); while(blank(ch) ) {ch=gc();} while(!blank(ch) ) {*s++=ch;ch=gc();} *s=0; } inline void gs(std::string &s){ char ch=gc();s+='#'; while(blank(ch) ) {ch=gc();} while(!blank(ch) ) {s+=ch;ch=gc();} } inline void ps(char *s){ while(*s!=0) pc(*s++); } inline void ps(const std::string &s){ for(auto it:s) if(it!='#') pc(it); } template<class T> inline void read(T &s){ s=0;char ch=gc();bool f=0; while(ch<'0'||'9'<ch) {if(ch=='-') f=1;ch=gc();} while('0'<=ch&&ch<='9') {s=s*10+(ch^48);ch=gc();} if(ch=='.'){ db p=0.1;ch=gc(); while('0'<=ch&&ch<='9') {s=s+p*(ch^48);ch=gc();} } s=f?-s:s; } template<class T,class ...A> inline void read(T &s,A &...a){ read(s);read(a...); } }; using IO::read; using IO::gs; using IO::ps; const int N=5e2+3; const int M=2e4+3; int Etot; int head[N],nxt[M]; struct Edge{ int u,v; }to[M]; inline void join(int u,int v){ nxt[++Etot]=head[u]; head[u]=Etot; to[Etot]={u,v}; } int n,m; int dfn[N],low[N],idx; int sta[N],top; int Ctot; std::vector<int>col[N]; inline void dfs1(int u,int f){ dfn[u]=low[u]=++idx; sta[++top]=u; for(int i=head[u];~i;i=nxt[i]){ int v=to[i].v; if(!dfn[v]){ dfs1(v,u); low[u]=std::min(low[u],low[v]); if(low[v]>=dfn[u]){ ++Ctot; while(1){ col[Ctot].push_back(sta[top]); if(sta[top]==v){ --top; break; } --top; } col[Ctot].push_back(u); } }else if(v!=f){ low[u]=std::min(low[u],dfn[v]); } } } int main(){ filein(a);fileot(a); read(n,m); Etot=-1; memset(head,-1,sizeof(head) ); for(int i=1;i<=m;++i){ int u,v; read(u,v); join(u,v);join(v,u); } for(int i=1;i<=n;++i){ if(!dfn[i]){ dfs1(i,i); } } for(int i=1;i<=Ctot;++i){ for(auto it:col[i]){ printf("%d ",it); }putchar('\n'); } return 0; }