Tarjan(强连通分量 割点 缩点)
Tarjan(强连通分量 割点 缩点)
Tarjan 算法
- 所需的变量
变量名 | |
---|---|
\(dfn[maxn]\) | 当前节点是第几个被访问到的 |
\(low[maxn]\) | 当前节点所能访问到的最小的dfn |
\(sta[maxn]\) | 存储可能构成强连通分量的栈 |
\(col[maxn]\) | 记录各个节点所属于的强连通分量编号 |
强连通分量
- 图中找到一个最大的图,使这个图中每个两点都能够互相到达。这个最大的图称为强连通分量,同时一个点也属于强连通分量。
缩点
把所有环按照染色情况缩成一个点,重新连边
首先链式前向星建原图,来一波tarjan缩点,然后再链式前向星建一个新图
最后来个topo求从入度为零的点到它能走到的点的最大值
缩点code
const int maxn = 2e5 + 10;
int n,m;
int a[maxn];
int dfn[maxn],head[maxn],cnt,col[maxn],sta[maxn],top,tot;
int dep,f[maxn],p,ans[maxn],in[maxn],low[maxn],vis[maxn];
std::vector<int> ed[maxn];
struct node{
int v,next,u;
}e[maxn<<1];
void add(int u,int v) {
e[++cnt].next = head[u];
e[cnt].v = v;
e[cnt].u = u;
head[u] = cnt;
}
void tarjan(int u) {
dfn[u] = low[u] = ++dep;
vis[u] = 1;
sta[++top] = u;
for(int i = head[u]; i ;i = e[i].next) {
int v = e[i].v;
if(!dfn[v]) {
tarjan(v);
low[u] = min(low[u],low[v]);
}
else {
if(vis[v])
low[u] = min(low[u],low[v]);
}
}
if(dfn[u] == low[u]) {
col[u] = u;
vis[u] = 0;
while(sta[top] != u) {
col[sta[top]] = u;
a[u] += a[sta[top]];
vis[sta[top--]] = 0;
}
top--;
}
}
int topo() {
queue<int> q;
for(int i = 1; i <= n; i++) {
if(!in[i] && col[i] == i) {
q.push(i);
f[i] = a[i];
}
}
while(!q.empty()) {
int u = q.front();
q.pop();
for(int i = 0; i < ed[u].size(); i++) {
int v = ed[u][i];
f[v] = max(f[v],f[u]+a[v]);
in[v]--;
if(in[v] == 0) {
q.push(v);
}
}
}
int sum = 0;
for(int i = 1; i <= n; i++) {
sum = max(sum,f[i]);
}
return sum;
}
int main() {
cin >> n >> m;
for(int i = 1; i <= n; i++) {
cin >> a[i];
}
for(int j = 1; j <= m; j++) {
int u,v;
cin >> u >> v;
add(u,v);
}
for(int i = 1; i <= n; i++) {
if(!dfn[i]) {
tarjan(i);
}
}
for(int i = 1; i <= m; i++) {
int x = col[e[i].u];
int y = col[e[i].v];
if(x != y) {
in[y]++;
ed[x].PB(y);
}
}
cout << topo() << "\n";
return 0;
}
割点和割桥
对于无向图有双连通分量
- 对于一个无向图,如果把一个点删除后这个图的极大连通分量数增加了,那么这个点就是这个图的割点(又称割顶)。
- 对于一个无向图,如果删掉一条边后图中的连通分量数增加了,则称这条边为桥或者割边
使用\(Tarjan\) 判断是否为割点
- 对于祖先,如果它有两个以上儿子,那么它必为割点
- 对于某个顶点\(u\),如果存在\(v(u的儿子)\) 使得\(low[v] >= dfn[u]\) 那么\(u\)为割点
割点code
const int maxn = 2e5 + 10;
int n,m;
struct node{
int v,next;
}e[maxn];
int head[maxn],cnt;
int dfn[maxn],low[maxn],ans[maxn];
int dep = 0,tot = 0;
void tarjan(int u,int fa) {
low[u] = dfn[u] = ++dep;
int an = 0;
for(int i = head[u]; i; i = e[i].next) {
int v = e[i].v;
if(!dfn[v]) {
tarjan(v,fa);
low[u] = min(low[u],low[v]);
if(u != fa && dfn[u] <= low[v]) {
ans[u] = 1;
}
if(u == fa) {
an++;
}
}
low[u] = min(low[u],dfn[v]);
}
if(an >= 2 && u == fa) {
ans[u] = 1;
}
}
void add(int u,int v) {
e[++cnt].next = head[u];
e[cnt].v = v;
head[u] = cnt;
}
int main() {
cin >> n >> m;
for(int i = 1; i <= m; i++) {
int u,v;
cin >> u >> v;
add(u,v);
add(v,u);
}
for(int i = 1; i <= n; i++) {
if(dfn[i] == 0) {
tarjan(i,i);
}
}
for(int i = 1; i <= n; i++) {
if(ans[i]) {
tot++;
}
}
cout << tot << "\n";
for(int i = 1; i <= n; i++) {
if(ans[i]) {
cout << i << " ";
}
}
cout << "\n";
return 0;
}