题目链接:https://www.nowcoder.com/acm/contest/81/C
题目:给n点、m条边(无自环、重边),取出最少点能跑到所有点,输出点数和升序输出这些点(如果有多种情况,输出字典序最小的点集)
tarjan 缩点裸题!
1、 修改模板两点即可! 添加u的回边连接点v已经不在栈里,即v已经属于一个确定的强连通分量 flag[v],这个分量即可删去,因为能通过其它点跑到!标记为p[flag[v]] =-1.
2、另外一个强连通分量出栈后,此时栈内不为空,表明上面有父点直达这个强连通分量,所
以删去此点p[flag[u]] =-1.
3、最终排序所有的强连通分量p(记录的强连通分量中最小下标),输出全部不为-1的p!
/*
wannafly 14 C、tanjar 缩点
*/
#include <iostream>
#include <stdio.h>
#include <algorithm>
#include <string.h>
#include <map>
#include <vector>
#include <set>
#define llt long long
using namespace std;
const int Size = 1e5+7;
vector <int> V[Size];
int DFN[Size],LOW[Size],vis[Size],q[Size];
int p[Size];//第i个强连通分量的最小表示
int flag[Size];//i节点属于第几个强连通分量
int cnt = 0;
//Tanjar 缩强连通分量
int toUsed = 0,num = 0;
void Tanjar(int u){
DFN[u] = ++toUsed;
LOW[u] = toUsed;
vis[u] = 1;
q[cnt++] = u;
for(int i=0;i<V[u].size();++i){
int v = V[u][i];
if(DFN[v]==0){
Tanjar(v);
LOW[u] = min(LOW[u],LOW[v]);//u通过儿子的回边能到达的最远祖先
}else if(vis[v]){//在栈内
LOW[u] = min(LOW[u],DFN[v]);//回边到达的节点
}else {//已经缩为点,入度不为0
p[flag[v]] = -1;
}
}
if(DFN[u]==LOW[u]){
p[num++] = u;
do{
int v = q[cnt-1];
vis[v] = 0;
flag[v] = num-1;
p[num-1] = min(p[num-1],v);
cnt--;
}while(q[cnt]!=u);
if(cnt!=0) p[num-1] = -1;
}
}
int main(){
int n,m;
scanf("%d%d",&n,&m);
for(int i=1;i<=m;++i){
int u,v;
scanf("%d%d",&u,&v);
V[u].push_back(v);
}
for(int i=1;i<=n;++i)
if(DFN[i]==0) Tanjar(i);
sort(p,p+num);
int j=0;
for(j=0;j<num;++j)
if(p[j]!=-1) break;
printf("%d\n",num-j);
for(j;j<num;++j) printf("%d%c",p[j],(j==num-1)?'\n':' ');
return 0;
}