强连通分量
是指在一张图图中任意两点都可以两两互相到达.
作用
在一些问题中因为两两可以互相到达的性质,可以将一个强连通分量当做一个大点来做.
Tarjan
与tarjan算法求割点的做法相似,遍历时记录一个时间戳和一个low记录不经过祖先节点可以到达到的时间戳最小的点,遍历时再将扫到的点放入一个栈中,并用一个布尔数组记录这些点.
若时间戳与low相等,则不断pop直到当前点被pop,并且pop掉的点在布尔数组中清零,这样避免了被重复利用,被pop掉的点属于同一个强连通分量.
代码
void dfs(int now)
{
int p,q;
tim[now]=low[now]=++tt;
use[now]=1;
sta.push(now);
for(p=first[now];p!=-1;p=bn[p].next)
{
if(!tim[bn[p].to])
{
dfs(bn[p].to);
low[now]=min(low[now],low[bn[p].to]);
}
else if(use[bn[p].to])
{
low[now]=min(low[now],tim[bn[p].to]);
}
}
if(low[now]==tim[now])
{
num[now]=++lt;
use[now]=0;
for(;sta.top()!=now;sta.pop())
{
num[sta.top()]=lt;
use[sta.top()]=0;
}
sta.pop();
}
}
例题 洛谷 P3387 【模板】缩点
题意
给定一个n个点m条边有向图,每个点有一个权值,求一条路径,使路径经过的点权值之和最大。允许多次经过一条边或者一个点,但是,重复经过的点,权值只计算一次。
做法
因为点,边都可以走多次,因此每个强连通分量都可以被看做一个大点,可以将原图中的所有点都缩成一个大点,再建新图,因为经过缩点,所以新图是一个DAG,再用拓扑排序的方法求最大点权和即可.
代码
#include<iostream>
#include<cstdio>
#include<cstring>
#include<stack>
#include<queue>
#define N 10010
#define M 100100
using namespace std;
int n,m,a[M],b[M],first[N],tim[N],low[N],num[N],tt,lt,bb,dn[N],sz[N],d[N],ds[N],ans;
bool use[N];
struct Bn
{
int to,next;
}bn[M];
stack<int>sta;
queue<int>que;
inline void add(int u,int v)
{
bb++;
bn[bb].to=v;
bn[bb].next=first[u];
first[u]=bb;
}
void dfs(int now)
{
int p,q;
tim[now]=low[now]=++tt;
use[now]=1;
sta.push(now);
for(p=first[now];p!=-1;p=bn[p].next)
{
if(!tim[bn[p].to])
{
dfs(bn[p].to);
low[now]=min(low[now],low[bn[p].to]);
}
else if(use[bn[p].to])
{
low[now]=min(low[now],tim[bn[p].to]);
}
}
if(low[now]==tim[now])
{
num[now]=++lt;
use[now]=0;
for(;sta.top()!=now;sta.pop())
{
num[sta.top()]=lt;
use[sta.top()]=0;
}
sta.pop();
}
}
int main()
{
memset(first,-1,sizeof(first));
int i,j,p,q;
cin>>n>>m;
for(i=1;i<=n;i++) scanf("%d",&dn[i]);
for(i=1;i<=m;i++)
{
scanf("%d%d",&a[i],&b[i]);
add(a[i],b[i]);
}
for(i=1;i<=n;i++) if(!tim[i]) dfs(i);
for(i=1;i<=n;i++) sz[num[i]]+=dn[i];
bb=0;
memset(first,-1,sizeof(first));
for(i=1;i<=m;i++)
{
if(num[a[i]]==num[b[i]]) continue;
add(num[a[i]],num[b[i]]);
ds[num[b[i]]]++;
}
for(i=1;i<=lt;i++)
{
if(!ds[i]) que.push(i),d[i]=sz[i];
}
for(;!que.empty();)
{
q=que.front();
que.pop();
for(p=first[q];p!=-1;p=bn[p].next)
{
d[bn[p].to]=max(d[bn[p].to],d[q]+sz[bn[p].to]);
ds[bn[p].to]--;
if(!ds[bn[p].to]) que.push(bn[p].to);
}
}
for(i=1;i<=lt;i++) ans=max(ans,d[i]);
cout<<ans;
}