强连通分量+缩点

news/2024/7/20 22:32:20 标签: 算法, 深度优先, 图论

[图论与代数结构 701] 强连通分量

题目描述

给定一张 n n n 个点 m m m 条边的有向图,求出其所有的强连通分量。

注意,本题可能存在重边和自环。

输入格式

第一行两个正整数 n n n m m m ,表示图的点数和边数。

接下来 m m m 行,每行两个正整数 u u u v v v 表示一条边。

输出格式

第一行一个整数表示这张图的强连通分量数目。

接下来每行输出一个强连通分量。第一行输出 1 号点所在强连通分量,第二行输出 2 号点所在强连通分量,若已被输出,则改为输出 3 号点所在强连通分量,以此类推。每个强连通分量按节点编号大小输出。

样例 #1

样例输入 #1

6 8
1 2
1 5
2 6
5 6
6 1
5 3
6 4
3 4

样例输出 #1

3
1 2 5 6
3
4

提示

对于所有数据, 1 ≤ n ≤ 10000 1 \le n \le 10000 1n10000 1 ≤ m ≤ 100000 1 \le m \le 100000 1m100000

#include <bits/stdc++.h>

using namespace std;
const int N = 2e5 + 5;
typedef long long ll;
typedef pair<ll, ll> pll;
typedef array<ll, 3> p3;
int mod = 998244353;
const int maxv = 4e6 + 5;
// #define endl "\n"

int n,m,tot,dfsn[N],ins[N],low[N];
stack<int> s;

vector<int> e[N];
vector< vector<int> > scc;
vector<int> b(N);
void dfs(int x)
{
	low[x]=dfsn[x]=++tot,ins[x]=1,s.push(x);
	for(auto u: e[x]){
		if(!dfsn[u]){
			dfs(u);
			low[x]=min(low[x],low[u]);
		}
		else if(ins[u]) low[x]=min(low[x],dfsn[u]);
	}
	if(dfsn[x]==low[x]){
		vector<int> c;
		while(1){
			auto t=s.top();
			c.push_back(t);
			ins[t]=0;
			s.pop();
			b[t]=scc.size();
			if(t==x) break;
		}
		sort(c.begin(),c.end());
		scc.push_back(c);
	}
}

void add(int u,int v)
{
	e[u].push_back(v);
}

void solve()
{	
	int n,m;
	cin>>n>>m;
	for(int i=1;i<=m;i++){
		int u,v;
		cin>>u>>v;
		add(u,v);
	}
	for(int i=1;i<=n;i++){
		if(!dfsn[i]){
			dfs(i);
		}
	}
	cout<<scc.size()<<endl;
	vector<int> vis(N);
	for(int i=1;i<=n;i++){
		int x=b[i];
		if(vis[x]) continue;
		vis[x]=1;
		for(auto r: scc[x]) cout<<r<<" ";
		cout<<endl;
	}
}

int main()
{
    ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
	int t;
	t=1;
	//cin>>t;
	while(t--){
		solve();
	}
    system("pause");
    return 0;
}

【模板】缩点

题目描述

给定一个 n n n 个点 m m m 条边有向图,每个点有一个权值,求一条路径,使路径经过的点权值之和最大。你只需要求出这个权值和。

允许多次经过一条边或者一个点,但是,重复经过的点,权值只计算一次。

输入格式

第一行两个正整数 n , m n,m n,m

第二行 n n n 个整数,其中第 i i i 个数 a i a_i ai 表示点 i i i 的点权。

第三至 m + 2 m+2 m+2 行,每行两个整数 u , v u,v u,v,表示一条 u → v u\rightarrow v uv 的有向边。

输出格式

共一行,最大的点权之和。

样例 #1

样例输入 #1

2 2
1 1
1 2
2 1

样例输出 #1

2

提示

对于 100 % 100\% 100% 的数据, 1 ≤ n ≤ 1 0 4 1\le n \le 10^4 1n104 1 ≤ m ≤ 1 0 5 1\le m \le 10^5 1m105 0 ≤ a i ≤ 1 0 3 0\le a_i\le 10^3 0ai103

我们对有向图进行缩点后,整个图就变为了DAG,即有向无环图,我们就可以在有向无环图上进行一些DP的操作,显然对于一个有向无环图,我们很容易得到这个题的状态转移:
d p [ i ] = m a x ( d p [ i ] , d p [ j ] + s [ i ] ) , s 为缩点后的点权, j 为 i 的前驱节点 dp[i]=max(dp[i],dp[j]+s[i]),s为缩点后的点权,j为i的前驱节点 dp[i]=max(dp[i],dp[j]+s[i])s为缩点后的点权,ji的前驱节点

#include <bits/stdc++.h>

using namespace std;
const int N = 2e5 + 5;
typedef long long ll;
typedef pair<ll, ll> pll;
typedef array<ll, 3> p3;
// int mod = 998244353;
const int maxv = 4e6 + 5;
// #define endl "\n"

int n, m, tot, dfsn[N], ins[N], low[N];
stack<int> s;
vector<int> e[N], e2[N];
vector<vector<int>> scc;
vector<int> b(N);
int a[N],z[N];

void dfs(int x)
{
	low[x] = dfsn[x] = ++tot, ins[x] = 1, s.push(x);
	for (auto u : e[x])
	{
		if (!dfsn[u])
		{
			dfs(u);
			low[x] = min(low[x], low[u]);
		}
		else if (ins[u])
			low[x] = min(low[x], dfsn[u]);
	}
	if (dfsn[x] == low[x])
	{
		vector<int> c;
		while (1)
		{
			auto t = s.top();
			c.push_back(t);
			ins[t] = 0;
			s.pop();
			b[t] = scc.size();
			z[scc.size()]+=a[t];
			if (t == x)
				break;
		}
		scc.push_back(c);
	}
}

void add(int u, int v)
{
	e[u].push_back(v);
}

void solve()
{
	cin >> n >> m ;
	for(int i=1;i<=n;i++) cin>>a[i];
	for (int i = 1; i <= m; i++)
	{
		int u, v;
		cin >> u >> v;
		add(u, v);
	}
	for (int i = 1; i <= n; i++)
	{
		if (!dfsn[i])
		{
			dfs(i);
		}
	}
	for(int i=1;i<=n;i++){
		for(auto u: e[i]){
			if(b[i]!=b[u]){
				e2[b[i]].push_back(b[u]);
			}
		}
	}
	vector<int> dp(N);
	vector<int> vis(N);
	int t=0;
	for(int i=0;i<scc.size();i++){
		dp[i]=z[i];
	}
	for(int i=scc.size()-1;i>=0;i--){
		t++;
		for(auto u: e2[i]){
			if(vis[u]!=t){
				vis[u]=t;
				if(dp[u]<dp[i]+z[u]){
					dp[u]=dp[i]+z[u];
				}
			}
		}
	}
	int ans=0;
	for(int i=0;i<scc.size();i++){
		ans=max(ans,dp[i]);
	}
	cout<<ans<<endl;
}

int main()
{
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
	int t;
	t = 1;
	// cin>>t;
	while (t--)
	{
		solve();
	}
	system("pause");
	return 0;
}

http://www.niftyadmin.cn/n/5091706.html

相关文章

《TypeScript》系列之对比JavaScript,TypeScript的优势

概述 TypeScript是微软公司开发的一种基于JavaScript语言的编程语言&#xff0c;它的目的并不是创造一种全新的语言&#xff0c;而是增强JavaScript的功能&#xff0c;使其更适合多人合作的企业级项目。TypeScript可以看做是JavaScript的超集&#xff0c;即它继承了后者的全部…

RabbitMQ从0到1完整学习笔记二:《高级篇》

目录 1. 发送者的可靠性 1.1.生产者连接重试机制 1.2.生产者确认机制&#xff08;发布确认&#xff09; 1.3.实现生产者确认 1.3.1.开启生产者确认 1.3.2.定义ReturnCallback 1.3.3.定义ConfirmCallback 拓展&#xff1a; confirm 模式细节处理 2.MQ的可靠性 2.1.数据持久化 2.…

【文档智能】多模态预训练模型及相关数据集汇总

前言 大模型时代&#xff0c;在现实场景中或者企业私域数据中&#xff0c;大多数数据都以文档的形式存在&#xff0c;如何更好的解析获取文档数据显得尤为重要。文档智能也从以前的目标检测&#xff08;版面分析&#xff09;阶段转向多模态预训练阶段&#xff0c;本文将介绍目…

阿里云韩国服务器测试IP地址及公网带宽收费价格表

阿里云服务器韩国&#xff08;首尔&#xff09;地域公网带宽价格表&#xff0c;1M带宽价格是23.0元/月&#xff0c;按使用流量1GB价格是0.8元&#xff0c;阿里云韩国服务器测试IP地址&#xff1a;149.129.12.20&#xff0c;阿里云百科aliyunbaike.com来详细说下阿里云韩国服务器…

【计算机网络笔记】数据交换之报文交换和分组交换

系列文章目录报文交换分组交换存储-转发报文交换 vs 分组交换总结 系列文章目录 什么是计算机网络&#xff1f; 什么是网络协议&#xff1f; 计算机网络的结构 数据交换之电路交换 报文交换 报文&#xff1a;源&#xff08;应用&#xff09;发送的信息整体。比如一个文件、一…

C语言,求质因数中的较大的值

首先要求出输入的质数的两个质因数&#xff0c;就要用到判断素数时用到的方法。用内循环产生2到n的数字&#xff0c;当求到了质因数之后&#xff0c;也要先用一个变量将质因数存起来&#xff0c;当后面遇到更大的质因数时&#xff0c;再将原来的质因数覆盖&#xff0c;如果更小…

【java学习—七】对象的实例化过程(33)

文章目录 1. 简单类对象的实例化过程2. 子类对象的实例化过程 1. 简单类对象的实例化过程 2. 子类对象的实例化过程

tracy 学习

https://github.com/wolfpld/tracy 适用于游戏和其他应用的实时、纳秒分辨率、远程控制、支持采样和帧率检测 Tracy 支持分析 CPU&#xff08;为 C、C 和 Lua 集成提供直接支持。同时&#xff0c;互联网上存在许多其他语言的第三方绑定&#xff0c;例如 Rust 、Zig、C # 、 OC…