其他分享
首页 > 其他分享> > tarjan大合辑

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