Tarjan模板

模板题在此
https://ac.nowcoder.com/acm/contest/86639/B

/*
tarjan:求强联通分量,本题中运用该算法实现重新构图并toposort
*/

#include<bits/stdc++.h>
#define IOS ios::sync_with_stdio(false); cin.tie(nullptr);
#define int long long
using namespace std;
const int Mod=998244353;
const int N=1e5+5;
int n;
int h[N],e[N*2],ne[N*2],idx;
int dfn[N],low[N],tot;
int stk[N],siz[N],top;
bool vis[N];
int col[N],cnt;
int in[N],dis[N];

vector<int> a(n+1);
vector<int> ee[N];
vector<int> en;

void add(int a,int b){
    e[++idx]=b;
    ne[idx]=h[a];
    h[a]=idx;
}

/*
强联通分量:"在图中找到一个最大的图,使这个图中每个两点都能够互相到达。这个最大的图称为强连通分量,同时一个点也属于强连通分量。"

(1)、dfn[],表示这个点在dfs时是第几个被搜到的。

(2)、low[],表示这个点以及其子孙节点连的所有点中dfn最小的值

(3)、stack[],表示当前所有可能能构成是强连通分量的点。

(4)、vis[],表示一个点是否在stack[ ]数组中。

(1)、首先初始化dfn[u]=low[u]=第几个被dfs到

dfn可以理解,但为什么low也要这么做呢?

 因为low的定义如上,也就是说如果没有子孙与u的祖先相连的话,dfn[u]一定是它和它的所有子孙中dfn最小的(因为它的所有子孙一定比他后搜到)。

 

(2)、将u存入stack[ ]中,并将vis[u]设为true

stack[ ]有什么用?

如果u在stack中,u之后的所有点在u被回溯到时u和栈中所有在它之后的点都构成强连通分量。

 

(3)、遍历u的每一个能到的点,如果这个点dfn[ ]为0,即仍未访问过,那么就对点v进行dfs,然后low[u]=min{low[u],low[v]}

low[ ]有什么用?

应该能看出来吧,就是记录一个点它最大能连通到哪个祖先节点(当然包括自己)

如果遍历到的这个点已经被遍历到了,那么看它当前有没有在stack[ ]里,如果有那么low[u]=min{low[u],low[v]}

如果已经被弹掉了,说明无论如何这个点也不能与u构成强连通分量,因为它不能到达u

如果还在栈里,说明这个点肯定能到达u,同样u能到达他,他俩强联通。

 

(4)、假设我们已经dfs完了u的所有的子树那么之后无论我们再怎么dfs,u点的low值已经不会再变了。

那么如果dfn[u]=low[u]这说明了什么呢?

再结合一下dfn和low的定义来看看吧

dfn表示u点被dfs到的时间,low表示u和u所有的子树所能到达的点中dfn最小的。

这说明了u点及u点之下的所有子节点没有边是指向u的祖先的了,即我们之前说的u点与它的子孙节点构成了一个最大的强连通图即强连通分量

此时我们得到了一个强连通分量,把所有的u点以后压入栈中的点和u点一并弹出,将它们的vis[ ]置为false,如有需要也可以给它们打上相同标记(同一个数字)

*/


void tarjan(int u){
    dfn[u]=low[u]= ++tot;
    stk[++top]=u;
    vis[u]=true;
    for(int i=h[u];i;i=ne[i]){
        int j=e[i];
        if(dfn[j]==0){
            tarjan(j);
            low[u]=min(low[u],low[j]);
        }
        else if(vis[j]){
            low[u]=min(low[u],dfn[j]);
        }
    }
    if(dfn[u] == low[u]){
        int y;
        cnt++;
        do{
            y=stk[top--];
            vis[y]=false;
            col[y]=cnt;
            siz[cnt]++;
        }while(u!=y);
    }
}

void topo(){
    queue<int> q;
    for(int i=0;i<=cnt;i++){
        if(in[i]==0){
            dis[i]=0;
            q.push(in[i]);
        }
    }
    
    while(q.size()){
        int t=q.front();
        q.pop();   
        for(auto it:ee[t]){
            if(!--in[it]) q.push(it);
        }
        en.push_back(t);
    }
}

signed main(){
	IOS;
	cin>>n;
    for(int i=1;i<=n;i++){
        cin>>a[i];
        add(i,a[i]);
    }
    //139行是为了防止有点没有跑到
    for(int i=1;i<=n;i++) if(!dfn[i]) tarjan(i);
    
    for(int i=1;i<=n;i++){
        for(int j=h[i];j;j=ne[j]){
            int t=e[j];
            if(col[i]!=col[t]){
                ee[col[i]].push_back(col[t]);
                in[col[t]]++;
            }
        }
    }
    
    for(int i=1;i<=cnt;i++){
        ee[0].push_back(i);
        in[i]++;
    }
    
    topo();
    
    for(auto u:en){
        for(auto v:ee[u]){
            int len=siz[v];
            if(dis[v]<dis[u]+len){
                dis[v]=dis[u]+len;
            }
        }
    }
    int mx=0;
    for(int i=1;i<=cnt;i++){
        mx=max(mx,dis[i]);
    }
    cout<<mx<<endl;
	return 0;
}

posted on 2024-10-28 16:10  TaopiTTT  阅读(1)  评论(0编辑  收藏  举报