tarjan大合辑
作者:互联网
\(tarjan\) 大合辑
1.割边:
#include<bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10, M = 1e6 + 10;
int hd[N], nt[M], to[M], tot = 1;/////// tot = 1反边
inline void add(int u, int v)
{
nt[++tot] = hd[u];
hd[u] = tot;
to[tot] = v;
}
int n, m;
int x; char v;
inline int read()
{
x = 0;
while(!isdigit(v)) v = getchar();
while(isdigit(v)) x = (x << 1) + (x << 3) + v - 48, v = getchar();
return x;
}
int dfn[N], low[N], now;
bool mark[N];//////////// 割边
inline void tarjan(int u, int fa)
{
dfn[u] = low[u] = ++now;
int v;
for(int e = hd[u]; e; e = nt[e])
if((v = to[e]) ^ fa)/////////// 无向图判回
{
if(!dfn[v])
{
tarjan(v, u);
low[u] = min(low[u], low[v]);
}
else low[u] = min(low[u], dfn[v]);
if(low[v] > dfn[u])///////////////// >
mark[e] = mark[e ^ 1] = true;/// 正反标记
}
}
int main()
{
n = read(), m = read();
for(int i = 1, u, v; i <= m; ++i)
u = read(), v = read(), add(u, v), add(v, u);
for(int i = 1; i <= n; ++i)
if(!dfn[i])
tarjan(i, 0);
return 0;
}
2.割点:
#include<bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10, M = 1e6 + 10;
int hd[N], nt[M], to[M], tot = 1;
inline void add(int u, int v)
{
nt[++tot] = hd[u];
hd[u] = tot;
to[tot] = v;
}
int n, m;
int x; char v;
inline int read()
{
x = 0;
while(!isdigit(v)) v = getchar();
while(isdigit(v)) x = (x << 1) + (x << 3) + v - 48, v = getchar();
return x;
}
int dfn[N], low[N], now;
bool mark[N];/////////////// 割点
int rt, res;
inline void tarjan(int u, int fa)
{
dfn[u] = low[u] = ++now;
int v, cnt = 0;
for(int e = hd[u]; e; e = nt[e])
if((v = to[e]) ^ fa)
{
if(!dfn[v])
{
tarjan(v, u);
low[u] = min(low[u], low[v]);
if(low[v] >= dfn[u])/////////// >=
{
++cnt;////////////////////
if(cnt > 1 || (rt ^ u))/// 根特殊处理
mark[u] = true;///////
}
}
else low[u] = min(low[u], dfn[v]);
}
}
int main()
{
n = read(), m = read();
for(int i = 1, u, v; i <= m; ++i)
u = read(), v = read(), add(u, v), add(v, u);
for(int i = 1; i <= n; ++i)
if(!dfn[i])
rt = i, tarjan(i, 0);///////// 赋rt
for(int i = 1; i <= n; ++i)
if(mark[i])
cout << i << ' ';
return 0;
}
3.边双 \(DCC\)
#include<bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10, M = 1e6 + 10;
int x; char v;
inline int read()
{
x = 0;
while(!isdigit(v)) v = getchar();
while(isdigit(v)) x = (x << 1) + (x << 3) + v - 48, v = getchar();
return x;
}
int n, m;
int hd[N], nt[M], to[M], tot = 1;
inline void add(int u, int v)
{
nt[++tot] = hd[u];
hd[u] = tot;
to[tot] = v;
}
int dfn[N], low[N], now;
vector <int> g[N];
int cl[N], co;
bool mark[N];
inline void tarjan(int u, int fa)/////////// 和割边一模一样
{
dfn[u] = low[u] = ++now;
int v;
for(int e = hd[u]; e; e = nt[e])
if((v = to[e]) ^ fa)
{
if(!dfn[v])
{
tarjan(v, u);
low[u] = min(low[u], low[v]);
if(low[v] > dfn[u])
mark[e] = mark[e ^ 1] = true, cout << u << ' ' << v << '\n';
}
else low[u] = min(low[u], dfn[v]);
}
}
inline void getDCC(int u)////////////////
{
cl[u] = co;//////////////////////////
int v;///////////////////////////////
for(int e = hd[u]; e; e = nt[e])/////
if(!mark[e] && !cl[v = to[e]])/// 找DCC,把桥断了即可
getDCC(v);//////////////////
}
int main()
{
n = read(), m = read();
for(int i = 1, u, v; i <= m; ++i)
u = read(), v = read(), add(u, v), add(v, u);
for(int i = 1; i <= n; ++i)
if(!dfn[i])
tarjan(i, 0);
for(int i = 1; i <= n; ++i)
if(!cl[i])
++co, getDCC(i);
for(int i = 1; i <= n; ++i) g[cl[i]].push_back(i);//////// 连通分量的搞
for(int i = 1, k; i <= co; ++i)//////////
{////////////////////////////////////////
cout << i << ':';////////////////////
cout << (k = g[i].size()) << " ";/// 连通分量输出板子
for(int j = 0; j < k; ++j)///////////
cout << g[i][j] << ' ';//////////
cout << '\n';////////////////////////
}
return 0;
}
/*
12 15
1 2
2 3
3 4
4 1
4 5
5 6
5 12
5 7
5 8
6 8
8 7
7 9
9 10
10 11
11 9*/
4.点双 \(BCC\)
#include<bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10, M = 1e6 + 10;
int x; char v;
inline int read()
{
x = 0;
while(!isdigit(v)) v = getchar();
while(isdigit(v)) x = (x << 1) + (x << 3) + v - 48, v = getchar();
return x;
}
int n, m;
int hd[N], nt[M], to[M], tot = 1;
inline void add(int u, int v)
{
nt[++tot] = hd[u];
hd[u] = tot;
to[tot] = v;
}
int dfn[N], low[N], now;
bool mark[N];
vector <int> in[N];
vector <int> g[N];
int stk[N], top, cl[N], co;
inline void tarjan(int u, int fa)
{
stk[++top] = u;
dfn[u] = low[u] = ++now;
int v;
for(int e = hd[u]; e; e = nt[e])
if((v = to[e]) ^ fa)
{
if(!dfn[v])
{
tarjan(v, u);
low[u] = min(low[u], low[v]);
if(low[v] >= dfn[u])
{
mark[u] = true;///////////////////////////////////
++co;///////////////////////////////////////////// 不能只搞u,不然丢失尾部割点
do{/////////////////////////////////////////////// 不能在外面搞割点,不然重复
if(mark[v = stk[top]]) in[v].push_back(co);/// 割点和普通点分开搞
else cl[v] = co;//////////////////////////////
} while(stk[top--] ^ u);///////////////////////// 弹栈 不弹割点
++top;/////////////////////////////////////////// 唯一一个不弹u的
}
}
else low[u] = min(low[u], dfn[v]);
}
}
int main()
{
n = read(), m = read();
for(int i = 1, u, v; i <= m; ++i)
u = read(), v = read(), add(u, v), add(v, u);
for(int i = 1; i <= n; ++i)
if(!dfn[i])
tarjan(i, 0);
for(int i = 1, k; i <= n; ++i)////////////
if(!mark[i]) g[cl[i]].push_back(i);///
else//////////////////////////////////
{///////////////////////////////////// 分割点和普通点分开高
k = in[i].size();/////////////////
for(int j = 0; j < k; ++j)////////
g[in[i][j]].push_back(i);/////
}/////////////////////////////////////
for(int i = 1, k; i <= co; ++i)
{
cout << i << ':';
cout << (k = g[i].size()) << " ";
for(int j = 0; j < k; ++j)
cout << g[i][j] << ' ';
cout << '\n';
}
return 0;
}
/*
12 15
1 2
2 3
3 4
4 1
4 5
5 6
5 12
5 7
5 8
6 8
8 7
7 9
9 10
10 11
11 9*/
5.强连通分量
#include<bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10, M = 1e6 + 10;
int x; char v;
inline int read()
{
x = 0;
while(!isdigit(v)) v = getchar();
while(isdigit(v)) x = (x << 1) + (x << 3) + v - 48, v = getchar();
return x;
}
int n, m;
int hd[N], nt[M], to[M], tot = 1;
inline void add(int u, int v)
{
nt[++tot] = hd[u];
hd[u] = tot;
to[tot] = v;
}
int dfn[N], low[N], now;
int stk[N], top, cl[N], co;
bool in[N];
vector <int> g[N];
inline void tarjan(int u)
{
dfn[u] = low[u] = ++now;
stk[++top] = u;
in[u] = true;
int v;
for(int e = hd[u]; e; e = nt[e])
{
if(!dfn[v = to[e]])
{
tarjan(v);
low[u] = min(low[u], low[v]);
}
else if(in[v]) low[u] = min(low[u], dfn[v]);
//////////// 要判断在栈中,因为有向图要保证能走回去
}
if(dfn[u] == low[u])/////////////////////////////
{
++co;//////////////////////////////////////// 最后弹
do {cl[v = stk[top]] = co; in[v] = false;}///
while(stk[top--] ^ u);///////////////////////
}
}
int main()
{
n = read(), m = read();
for(int i = 1, u, v; i <= m; ++i)
u = read(), v = read(), add(u, v);
for(int i = 1; i <= n; ++i)
if(!dfn[i])
tarjan(i);
for(int i = 1; i <= n; ++i)
g[cl[i]].push_back(i);
for(int i = 1, k; i <= co; ++i)
{
cout << i << ':';
cout << (k = g[i].size()) << " ";
for(int j = 0; j < k; ++j)
cout << g[i][j] << ' ';
cout << '\n';
}
return 0;
}
/*
5 8
1 3
1 2
3 5
3 4
3 2
4 5
4 1
5 1*/
标签:tarjan,大合辑,int,++,read,while,dfn,low 来源: https://www.cnblogs.com/fakeryu/p/16339779.html