集训太棒了
首先,强连通分量的定义:
有向图内,一个强连通分量内任一点之间豆村咋至少一条路径。
直观一点?
一个点集,在有向图上它们构成了一个环(个人理解但的确是这样)
一些定义:
Dfn(u)是节点u的时间戳,表示点u是第几个被访问的节点。
Low(u)是u或u的子树能够追溯到的最早的栈中节点的时间戳。
当Dfn(u)=Low(u)时,以u为根的搜索子树上所有节点(从栈顶到u的所有节点)是一个强连通分量。
Low数组的计算方法: 如果x→y是正向边(连接到不在栈中节点的边),
y是x的子树,所以y最早能去哪x最早就能去哪
那么 low[x]=min(low[x],low[y])
如果x→y是反向边(连接到已在栈中节点的边),这时候我们发现构成环了
那么 low[x]=min(low[x],dfn[y])
如果x→y是交叉边(连接到已经出栈的节点的边),
什么也不干
不理解?
code:
int n,m; #define maxn 10009 vector<int> son[maxn]; int tim,size[maxn],belong[maxn],scc_cnt,cnt; bool bein[maxn]; int st[maxn]; int dfn[maxn],low[maxn]; void Tarjan(int rt) { dfn[rt]=low[rt]=++tim; st[++cnt]=rt; for(int i=0;i<son[rt].size();i++) { int to=son[rt][i]; if(!dfn[to])//正向边 { Tarjan(to); low[rt]=min(low[rt],low[to]); }else if(!belong[to])//反向边 { low[rt]=min(low[rt],dfn[to]);//能不能取到一个更早的点 } } if(dfn[rt]==low[rt]) { //关键节点!! scc_cnt++; int k; do{ k=st[cnt--]; belong[k]=scc_cnt; size[scc_cnt]++; }while(k!=rt); } }