zoj3795 强联通

题意:给定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 ;
}



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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值