图的联通
求无向图割点,割边,双连通分量;求有向图强连通分量,缩点。
强联通分量
#include<bits/stdc++.h>
#define MN 4000010
#define pb push_back
using namespace std;
int n, m, vis[MN], stk[MN], top, cnt;
int dfn[MN], low[MN], bel[MN], tc;
int a[MN], sp[MN], f[MN], in[MN], ans;
int hd[MN], to[MN], nxt[MN], tot=1;
vector<int> ee[MN]; queue<int> q;
void eadd(int u,int v) {
to[++tot]=v;
nxt[tot]=hd[u];
hd[u]=tot;
}
void tarjan(int x) {
low[x]=dfn[x]=++tc;
vis[x]=1, stk[++top]=x;
for(int i=hd[x]; i; i=nxt[i]) {
int y=to[i];
if(vis[y]==2) continue;
if(!vis[y]) tarjan(y);
low[x]=min(low[x],low[y]);
}
if(dfn[x]==low[x]) {
int p; cnt++;
while(stk[top+1]!=x) {
p=stk[top--];
vis[p]=2, bel[p]=cnt;
}
}
}
signed main() {
ios::sync_with_stdio(0);
cin.tie(0);
cin >> n >> m;
for(int i=1; i<=n; ++i)
cin >> a[i];
while(m--) {
int u, v, w;
cin >> u >> v;
eadd(u,v);
}
for(int i=1; i<=n; ++i)
if(!dfn[i]) tarjan(i);
return 0;
}
割点
判断法则(?
-
根,至少两个子树
-
非根,存在 \(dfn_x\leq low_y\)
感性理解一下(雾。
#include<bits/stdc++.h>
#define MN 100010
#define pb push_back
using namespace std;
int n, m, tc, buc[MN], rt;
int dfn[MN], low[MN], cnt;
vector<int> to[MN];
void tarjan(int x) {
dfn[x]=low[x]=++tc;
int son=0;
for(int i=0; i<to[x].size(); ++i) {
int y=to[x][i];
if(dfn[y]) low[x]=min(low[x],dfn[y]);
else {
son++, tarjan(y);
low[x]=min(low[x],low[y]);
if(low[y]>=dfn[x]&&x!=rt)
cnt+=(!buc[x]), buc[x]=1;
}
}
if(son>=2&&x==rt) cnt+=(!buc[x]), buc[x]=1;
}
signed main() {
ios::sync_with_stdio(0);
cin.tie(0);
cin >> n >> m;
while(m--) {
int u, v;
cin >> u >> v;
to[u].pb(v);
to[v].pb(u);
}
for(int i=1; i<=n; ++i)
if(!dfn[i]) rt=i, tarjan(i);
cout << cnt << endl;
for(int i=1; i<=n; ++i)
if(buc[i]) cout << i << " ";
return 0;
}
如果是割边把等号去掉。
点双联通
求的时候往 stack 里塞,注意判断单独一个点。
#include<bits/stdc++.h>
#define MN 4000010
#define pb push_back
using namespace std;
int n, m, tc, stk[MN], top;
int dfn[MN], low[MN], cnt, rt;
int hd[MN], to[MN], nxt[MN], tot;
vector<int> bcc[MN];
void eadd(int u,int v) {
to[++tot]=v;
nxt[tot]=hd[u];
hd[u]=tot;
}
void tarjan(int x) {
dfn[x]=low[x]=++tc;
stk[++top]=x;
int son=0;
for(int i=hd[x]; i; i=nxt[i]) {
int y=to[i];
if(dfn[y]) low[x]=min(low[x],dfn[y]);
else {
son++, tarjan(y);
low[x]=min(low[x],low[y]);
if(low[y]>=dfn[x]) {
bcc[++cnt].pb(x);
while(stk[top+1]!=y) bcc[cnt].pb(stk[top--]);
}
}
}
if(son==0&&x==rt) bcc[++cnt].pb(x);
}
signed main() {
ios::sync_with_stdio(0);
cin.tie(0);
cin >> n >> m;
while(m--) {
int u, v;
cin >> u >> v;
eadd(u,v);
eadd(v,u);
}
for(int i=1; i<=n; ++i)
if(!dfn[i]) rt=i, tarjan(i);
cout << cnt << endl;
for(int i=1; i<=cnt; ++i) {
cout << bcc[i].size() << " ";
for(int j=0; j<bcc[i].size(); ++j)
cout << bcc[i][j] << " ";
cout << endl;
}
return 0;
}
边双联通
把割边去掉然后 dfs 就好了 qwq
#include<bits/stdc++.h>
#define MN 4000010
#define pb push_back
using namespace std;
int n, m, vis[MN], is[MN], tc;
int dfn[MN], low[MN], cnt;
int hd[MN], to[MN], nxt[MN], tot=1;
vector<int> bcc[MN];
void eadd(int u,int v) {
to[++tot]=v;
nxt[tot]=hd[u];
hd[u]=tot;
}
void tarjan(int x,int fa) {
dfn[x]=low[x]=++tc;
int son=0;
for(int i=hd[x]; i; i=nxt[i]) {
int y=to[i];
if(y==fa) continue;
if(dfn[y]) low[x]=min(low[x],dfn[y]);
else {
son++, tarjan(y,x);
low[x]=min(low[x],low[y]);
if(low[y]>dfn[x]) is[i]=is[i^1]=1;
}
}
}
void dfs(int p) {
bcc[cnt].pb(p);
for(int i=hd[p]; i; i=nxt[i]) {
int y=to[i];
if(vis[y]||is[i]) continue;
vis[y]=1, dfs(y);
}
}
signed main() {
ios::sync_with_stdio(0);
cin.tie(0);
cin >> n >> m;
while(m--) {
int u, v;
cin >> u >> v;
eadd(u,v);
eadd(v,u);
}
for(int i=1; i<=n; ++i)
if(!dfn[i]) tarjan(i,0);
for(int i=1; i<=n; ++i)
if(!vis[i]) vis[i]=1, cnt++, dfs(i);
cout << cnt << endl;
for(int i=1; i<=cnt; ++i) {
cout << bcc[i].size() << " ";
for(int j=0; j<bcc[i].size(); ++j)
cout << bcc[i][j] << " ";
cout << endl;
}
return 0;
}