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;
}