Wannafly挑战赛14 C.可达性(tarjan缩点)

题目描述

给出一个 0 ≤ N ≤ 10 5 点数、0 ≤ M ≤ 10 5 边数的有向图,
输出一个尽可能小的点集,使得从这些点出发能够到达任意一点,如果有多个这样的集合,输出这些集合升序排序后字典序最小的。
输入描述:
第一行为两个整数 1 ≤ n, m ≤ 10 5,接下来 M 行,每行两个整数 1 ≤ u, v ≤ 10 5表示从点 u 至点 v 有一条有向边。数据保证没有重边、自环。
输出描述:
第一行输出一个整数 z,表示作为答案的点集的大小;
第二行输出 z 个整数,升序排序,表示作为答案的点集。
示例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
题意
如上
题解
求有向图的最大强连通加上缩点,输出缩完后每个强连通子集的最小的点即可
强连通分量:有向图强连通分量:在有向图G中,如果两个顶点vi,vj间(vi>vj)有一条从vi到vj的有向路径,同时还有一条从vj到vi的有向路径,则称两个顶点强连通。如果有向图G的每两个顶点都强连通,称G是一个强连通图。有向图的极大强连通子图,称为强连通分量
缩点:因为强连通分量中的每两个点都是强连通的,可以将一个强连通分量当做一个超级点
代码
 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 const int N=1e5+5;
 4 vector<int>G[N],id[N];
 5 
 6 int low[N],dfn[N],instack[N],st[N],tot,cnt,scc,p;
 7 int belong[N],in[N];
 8 void tarjan(int u)
 9 {
10     int v;
11     low[u]=dfn[u]=++tot;//时间戳
12     st[++cnt]=u;//入栈
13     instack[u]=1;
14     for(int i=0;i<G[u].size();i++)
15     {
16         v=G[u][i];
17         if(!dfn[v])//是否已经访问
18         {
19             tarjan(v);
20             low[u]=min(low[u],low[v]);
21         }
22         else if(instack[v])//已经访问过并且在栈里
23         {
24             low[u]=min(low[u],dfn[v]);
25         }
26     }
27     if(low[u]==dfn[u])
28     {
29         scc++;//强连通
30         do{
31             v=st[cnt--];//出栈
32             instack[v]=0;//v出栈后
33             belong[v]=scc;//v属于哪个强连通1-scc
34             id[scc].push_back(v);//当前强连通的子集
35         }while(u!=v);
36     }
37 }
38 int main()
39 {
40     int n,m,u[N],v[N];
41     scanf("%d%d",&n,&m);
42     for(int i=1;i<=m;i++)
43     {
44         scanf("%d%d",&u[i],&v[i]);
45         G[u[i]].push_back(v[i]);
46     }
47     for(int i=1;i<=n;i++)//求所有强连通
48         if(!dfn[i])//是否已经访问过
49             tarjan(i);
50     for(int i=1;i<=m;i++)//缩点
51     {
52         if(belong[u[i]]==belong[v[i]])continue;
53         in[belong[v[i]]]++;
54     }
55     for(int i=1;i<=scc;i++)
56         sort(id[i].begin(),id[i].end());
57     vector<int> res;
58     for(int i=1;i<=scc;i++)
59         if(!in[i])
60             res.push_back(id[i][0]);
61     printf("%d\n%d",res.size(),res[0]);
62     for(int i=1;i<res.size();i++)
63         printf(" %d",res[i]);
64     return 0;
65 }

转载于:https://www.cnblogs.com/taozi1115402474/p/8899331.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值