Strongly Connected Components
由于是临时复习,所以会很简略
tarjan 求强连通分量
首先可以对图dfs搜索,得到一棵搜索树。
考虑在搜索过程魔改一下dfs来求强连通分量。
搜索的时候我们会遇到很多非树边,而强连通分量的产生显然和这些非树边有关系。
dfn的解释略。
每次访问到一个节点把他加入到一个栈中。
用 $low_{u}$ 表示节点 $u$ 能到达的最小的并且在栈中的 $dfn_{v}$。
考虑怎么求。
如果当前的 $v$ 未访问,那么 $v$ 能到达的点肯定都能更新 $low_{u}$,所以 $low_{u} = \min(low_{u}, low_{v})$。
如果访问过,那么看是否在栈中(根据定义),如果在,那么显然是 $low_{u} = \min(low_{u}, dfn_{v})$。
这个时候如果发现一个点 $u$,它的 $dfn_{u} = low_{u}$,那么它肯定是会产生一个新的强连通分量的,因为无论怎么走它都都和 $dfn$ 更小的节点不连通。
现在想一下它和哪些点同属于一个强连通分量。
假设现在考虑的是目前搜到的第一个强连通分量,那么显然是当前栈中的所有节点。
可以用反证法证明。
然后把这些点从栈中弹出。
如果搜到了下一个强连通分量,那肯定不会包含上一个强连通分量的点,同样是当前栈中的所有节点。
所以这同时解释了为什么 $low$ 的定义要看节点是否在栈中。
Code:
//因为太难看所以就把模板删掉了qwq
int n, m, u, v, cnt, scccnt, top, back, ans, q[10001], st[10001], dfn[10001], low[10001], a[10001], sum[10001], scc[10001], d[10001], dp[10001];
vector<int> g[10001], e[10001];
set<int> edge[10001];
void tarjan(int now) {
low[now] = dfn[now] = ++cnt;
st[++top] = now;
fo(i, g[now]) {
if(!dfn[i]) {
tarjan(i);
low[now] = min(low[now], low[i]);
}
else if(!scc[i]) low[now] = min(low[now], dfn[i]);
}
if(dfn[now] == low[now]) {
scc[now] = ++scccnt;
sum[scccnt] += a[now];
while(st[top] != now) {
scc[st[top]] = scccnt;
sum[scccnt] += a[st[top]];
--top;
}
--top;
}
}
int main() {
read(n, m);
fu(i, 1, n) read(a[i]);
while(m--) {
read(u, v);
g[u].push_back(v);
}
fu(i, 1, n) {
if(!scc[i]) {
tarjan(i);
}
}
fu(i, 1, n) {
fo(j, g[i]) {
if(scc[i] != scc[j] && !edge[scc[i]].count(scc[j])) {
e[scc[i]].push_back(scc[j]);
edge[scc[i]].insert(scc[j]);
++d[scc[j]];
}
}
}
fu(i, 1, scccnt) {
if(!d[i]) q[++back] = i;
}
fu(front, 1, back) {
u = q[front];
dp[u] += sum[u];
ans = max(ans, dp[u]);
fo(i, e[u]) {
dp[i] = max(dp[i], dp[u]);
if(!(--d[i])) q[++back] = i;
}
}
write(ans);
flush();
return 0;
}
本文来自博客园,作者:A_box_of_yogurt,转载请注明原文链接:https://www.cnblogs.com/A-box-of-yogurt/p/18016425