链接:
https://www.nowcoder.com/acm/contest/81/C
来源:牛客网
来源:牛客网
题目描述
给出一个 0 ≤ N ≤ 10
5 点数、0 ≤ M ≤ 10
5 边数的有向图,
输出一个尽可能小的点集,使得从这些点出发能够到达任意一点,如果有多个这样的集合,输出这些集合升序排序后字典序最小的。
输出一个尽可能小的点集,使得从这些点出发能够到达任意一点,如果有多个这样的集合,输出这些集合升序排序后字典序最小的。
输入描述:
第一行为两个整数 1 ≤ n, m ≤ 105, 接下来 M 行,每行两个整数 1 ≤ u, v ≤ 105 表示从点 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
题解:先Tarjan缩点后,入度为0的点,是一定要选取的, 在点集里选个min即可。
#include <bits/stdc++.h>
using namespace std;
#define SZ(X) ((int)X.size())
#define mp make_pair
#define pb push_back
#define RALL(X) X.rbegin(),X.rend()
#define ALL(X) X.begin(),X.end()
using ll = long long ;
using ld = long double ;
const int N = 1E5 + 7;
int b[N];
vector<int>G[N];
int sta[N<<2];
bool instack[N];
int dfn[N], low[N], belong[N];
int in[N];
int n, m, bcnt, index_, sz;
void tarjan(int u)
{
instack[u] = true;
dfn[u] = low[u] = ++ index_;
sta[++sz] = u;
for(int i = 0; i < SZ(G[u]); i ++) {
int v = G[u][i];
if(!dfn[v]) {
tarjan(v);
low[u] = min(low[u], low[v]);
} else if(instack[v]) {
low[u] = min(low[u], dfn[v]);
}
}
if(dfn[u] == low[u]) {
bcnt ++;
int v;
do {
v = sta[sz --];
instack[v] = false;
belong[v] = bcnt;
} while(u != v);
}
}
int main()
{
scanf("%d %d", &n, &m);
for(int i = 1; i <= m; i ++) {
int a, b;
scanf("%d %d", &a, &b);
G[a].pb(b);
}
for(int i = 1; i <= n; i ++) {
if(!dfn[i]) tarjan(i);
b[i] = 1E8;
}
for(int i = 1;i <= n;i ++) {
for(auto it : G[i]) {
if(belong[it] != belong[i]) in[belong[it]] ++;
}
}
for(int i = 1;i <= n;i ++) b[belong[i]] = min(i, b[belong[i]]);
vector<int>res;
for(int i = 1;i <= bcnt;i ++) {
if(in[i] == 0) res.pb(b[i]);
}
sort(ALL(res));
printf("%d\n", SZ(res));
for(int i = 0;i < SZ(res);i ++) {
printf("%d%c",res[i],i==SZ(res)-1?'\n':' ');
}
return 0;
}