可达性(tarjin算法 + 强联通分类 + 缩点)

A-可达性_2022图论班第二章连通性例题与习题 (nowcoder.com)

题目描述公
给出一个0SN≤105点数、0≤M≤105边数的有向图,
输出一个尽可能小的点集,使得从这些点出发能够到达任意一点,如果有多个这样的集合,输出这些集合升序排序后字典序最小的。
输入描述:
第一行为两个整数1 ≤n,m ≤ 105,
接下来M行,每行两个整数1≤ u, v ≤105表示从点u至点v有一条有向边。数据保证没有重边、自环。
输出描述:
第一行输出一个整数z,表示作为答案的点集的大小;第二行输出z个整数,升序排序,表示作为答案的点集。
示例1
示例1
输入
复制
7 10
4 5
5 1
2 5
6 5
7 2
4 2
1 2
5 3
3 5
3 6
输出
复制
2
4 7

题解:

对于一个强联通分量里是可以到达其中的任何一个点,根据题意要求字点序最小,所以我们找到下标最小的点,把这个强连通分量看作这一个点

对于什么点是到不了的呢,由于这是一个有向图

当一个点的入度为0,就没有点可以到他了,所以我们应该找到在缩点至之后,

所有入度为0的点,排序即为所求

值得注意的是,由于一个强联通分量看做一个点,如果x指向y,但是x与y属于一个强联通分量,那么y的入度不会加,因为我们已经把他们看作为一个点了

#include<iostream>
#include<algorithm>
#include<string>
#include<queue>
#include<vector>
#include<map>
#include<cstring>
#include<cmath>
#include<stack>
#include<set>
using namespace std;
#define int long long
typedef pair<int,int> PII;
int n,m;
vector<int> p[200050];
int dfn[200050];
int low[200050];
int cnt = 0;
int vis[200050];
stack<int> s;
int color[200050];
int pre[200050];
int idx = 0;
int in[200050];
void tarjin(int u)
{
	dfn[u] = low[u] = ++cnt;
	vis[u] = 1;
	s.push(u);
	for(auto v:p[u])
	{
		if(!dfn[v])
		{
			tarjin(v);
			low[u] = min(low[u],low[v]);
		}
		else if(vis[v])
		{
			low[u] = min(low[u],dfn[v]);
		}
	}
	if(dfn[u] == low[u])
	{
		idx++;
		while(1)
		{
			int v = s.top();
			s.pop();
			color[v] = idx;
			pre[idx] = min(v,pre[idx]);
			vis[v] = 0;
			if(v == u)
			break;
		}
	}
	
}
void solve()
{
	cin >> n >> m;
	memset(pre,0x3f,sizeof pre);
	for(int i = 1;i <= m;i++)
	{
		int x,y;
		cin >> x >> y;
		p[x].push_back(y);
	}
	for(int i = 1;i <= n;i++)
	{
		if(!dfn[i])
		{
			tarjin(i);
		}
	}
	for(int i = 1;i <= n;i++)
	{
		for(auto j:p[i])
		{
			int u = color[i];
			int v = color[j];
			if(u!=v)
			in[v] ++;
		}
	}
	vector<int> ans;
	for(int i = 1;i <= idx;i++)
	{
		if(!in[i])
		{
			ans.push_back(pre[i]);
		}
	}
	sort(ans.begin(),ans.end());
	cout << ans.size() <<"\n";
	for(auto k :ans)
	{
		cout <<k <<" ";
		
	}
}
//1 2 3 4 5
signed main(){
//	ios::sync_with_stdio(false);
//	cin.tie(0);
//	cout.tie(0);
	int t = 1;
//	cin >> t;
    while(t--)
	{

		solve();
	} 
}
//5 2
//3 12

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值