题意:给定n个点,m条有向边。
把点分成几个集合使得每个集合中的任意2点都不可达(一个集合只存放一个点也可以)
问最少需要分成几个集合。
如果没有环,则这个题目就是求有向图的最长链 。
但是有环,所以把环缩点成新点x,而点x的点权就是x点在原图中对应的顶点个数。
缩点后就是有向无环图,找个最长路
const int N = 100010 ;
const int M = 301000 ;
struct Edge{
int u , v , next ;
bool sign ; //是否为桥
}e[N<<1];
int lis[N] , id ;
void add(int u , int v){
e[id].sign = 0 ;
e[id].u = u ;
e[id].v = v ;
e[id].next = lis[u] ;
lis[u] = id++ ;
}
int dfn[N] , low[N] ,stak[N] , top , Time ;
int taj ; //分量标号
int belong[N] ; //所属分量
bool instack[N] ;
vector<int> bcc[N] ; //分量所包含点
void tarjan(int u , int father){
dfn[u] = low[u] = ++Time ;
stak[top++] = u ;
instack[u] = 1 ;
for(int i = lis[u] ; i != -1 ; i = e[i].next){
int v = e[i].v ;
if(dfn[v] == -1){
tarjan(v , u) ;
low[u] = min(low[u] , low[v]) ;
if(dfn[u] < low[v])
e[i].sign = 1 ; //桥
}
else if(instack[v])
low[u] = min(low[u] , dfn[v]) ;
}
if(low[u] == dfn[u]){
int now ;
taj++ ;
bcc[taj].clear() ;
do{
now = stak[--top] ;
instack[now] = 0 ;
belong[now] = taj ;
bcc[taj].push_back(now) ;
}while(now != u) ;
}
}
void init(){
memset(dfn , -1 , sizeof(dfn)) ;
memset(instack , 0 , sizeof(instack)) ;
memset(lis , -1 , sizeof(lis)) ;
id = 0 ;
top = Time = taj = 0 ;
}
int n , m ;
vector<int> g[N] ;
int in[N] ;
int dist[N] ;
int bfs(){
int i , u , v ;
for(i = 1 ; i <= taj ; i++){
g[i].clear() ;
in[i] = 0 ;
}
for(i = 0 ; i < id ; i++){
u = belong[e[i].u] ;
v = belong[e[i].v] ;
if(u != v){
g[u].push_back(v) ;
in[v]++ ;
}
}
queue<int> q ;
for(i = 1 ; i <= taj ; i++){
if(in[i] == 0){
q.push(i) ;
dist[i] = bcc[i].size() ;
}
else dist[i] = 0 ;
}
while(! q.empty()){
u = q.front() ; q.pop() ;
for(i = 0 ; i < g[u].size() ; i++){
v = g[u][i] ;
if(dist[u] + bcc[v].size() > dist[v])
dist[v] = dist[u] + bcc[v].size() ;
if(--in[v] == 0) q.push(v) ;
}
}
return *max_element(dist+1 , dist+1+taj) ;
}
int main(){
int i , u , v ;
while(scanf("%d%d" ,&n ,&m) != EOF){
init() ;
for(i = 1 ; i <= m ; i++){
scanf("%d%d" , &u , &v) ;
if(u != v) add(u , v) ;
}
for(i = 1 ; i <= n ; i++){
if(dfn[i] == -1) tarjan(i , i) ;
}
printf("%d\n" , bfs()) ;
}
return 0 ;
}