图论:tarjan相关算法复习

参考博客:
https://blog.csdn.net/izumi_hanako/article/details/78082544

我以前的博客:
https://blog.csdn.net/Cold_Chair/article/details/79918157


1.割点

对于一个无向连通图,如果有一个点\(x\),删掉它之后剩下的点就不连通了,它就是割点。

随便以一个点\(st\)开始做tarjan,对于非起点的点\(x \neq st\),如果有一个儿子\(y\in son[x]\)满足\(low[y]>=dfn[x]\),即\(y\)上不去了,那么\(x\)就是割点。

对于起点\(st\),如果它在tarjan时有两个及以上的子树,它就是割点。


2. 桥边

对于一个无向连通图,如果有一个边\((x,y)\),删掉它之后就不连通了,它就是桥边。

tarjan时有一条边:\(x->y\),若\(low[y]>dfn[x]\),则\((x,y)\)是桥边。


3. 有向图缩强联通分量

dfs树上有三种边:

  1. 树边
  2. 返祖边
  3. 横插边

在求\(low\)时,横插边忽略,只考虑树边和返祖边,所以需要记录哪些点在栈里。

做完一个点\(x\)的子树后,若\(low[x] \ge dfn[x]\),则栈顶到\(x\)的点可以缩成一个强联通分量。


4. 无向图缩边双

无向图没有横插边。

同有向图缩强联通分量。

注意遍历树时不能父亲过来的反向边,所以dfs时多记一下从哪条边来的。


5. 无向图缩点双(圆方树)

需要理解找桥边的过程先。

如果有一个树边\(x->y\),满足\(low[y] \ge dfn[x]\),那么目前栈顶到\(y\)的点和\(x\)就形成了一个点双。

注意同样不能走父边。


代码:

1.https://www.luogu.com.cn/problem/P3388

#include<bits/stdc++.h>
#define fo(i, x, y) for(int i = x, _b = y; i <= _b; i ++)
#define ff(i, x, y) for(int i = x, _b = y; i <  _b; i ++)
#define fd(i, x, y) for(int i = x, _b = y; i >= _b; i --)
#define ll long long
#define pp printf
#define hh pp("\n")
using namespace std;

const int N = 2e5 + 5;

int n, m, x, y;
int fi[N], to[N * 2], nt[N * 2], tot = 1;

void link(int x, int y) {
	nt[++ tot] = fi[x], to[tot] = y, fi[x] = tot;
}

int low[N], dfn[N], dfn0;
int son[N], ans[N];

void dg(int x, int la) {
	low[x] = dfn[x] = ++ dfn0;
	for(int i = fi[x]; i; i = nt[i]) if(i != la) {
		int y = to[i];
		if(!dfn[y]) {
			dg(y, i ^ 1);
			low[x] = min(low[x], low[y]);
			son[x] ++;
			if(low[y] >= dfn[x]) ans[x] = 1;
		} else low[x] = min(low[x], dfn[y]);
	}
	if(la == 0 && son[x] <= 1) ans[x] = 0;
}

int main() {
	scanf("%d %d", &n, &m);
	fo(i, 1, m) {
		scanf("%d %d", &x, &y);
		link(x, y); link(y, x);
	}
	fo(i, 1, n) if(!dfn[i])
		dg(i, 0);
	int s0 = 0;
	fo(i, 1, n) s0 += ans[i];
	pp("%d\n", s0);
	fo(i, 1, n) if(ans[i]) pp("%d ", i);
}

3.https://www.luogu.com.cn/problem/P3387

#include<bits/stdc++.h>
#define fo(i, x, y) for(int i = x, _b = y; i <= _b; i ++)
#define ff(i, x, y) for(int i = x, _b = y; i <  _b; i ++)
#define fd(i, x, y) for(int i = x, _b = y; i >= _b; i --)
#define ll long long
#define pp printf
#define hh pp("\n")
using namespace std;

const int N = 1e5 + 5;

int n, m, a[N], x, y;
int fi[N], nt[N], to[N], tot;

void link(int x, int y) {
	nt[++ tot] = fi[x], to[tot] = y, fi[x] = tot;
}

int low[N], dfn[N], dfn0;
int bz[N], z[N], z0;

int id[N], id0, v[N];

vector<int> p[N], e[N];
#define pb push_back
#define si size()

void dg(int x) {
	low[x] = dfn[x] = ++ dfn0;
	z[++ z0] = x; bz[x] = z0;
	
	for(int i = fi[x]; i; i = nt[i]) {
		int y = to[i];
		if(!dfn[y]) {
			dg(y);
			low[x] = min(low[x], low[y]);
		} else if(bz[y]) low[x] = min(low[x], dfn[y]);
	}
	
	if(low[x] >= dfn[x]) {
		id0 ++;
		do {
			bz[z[z0]] = 0;
			id[z[z0]] = id0;
			p[id0].pb(z[z0]);
			v[id0] += a[z[z0]];
		} while(z[z0 --] != x);
	}
}

int r[N], d[N], d0;

int f[N];

int main() {
	scanf("%d %d", &n, &m);
	fo(i, 1, n) scanf("%d", &a[i]);
	fo(i, 1, m) {
		scanf("%d %d", &x, &y);
		link(x, y);
	}
	fo(i, 1, n) if(!dfn[i])
		dg(i);
	fo(i, 1, id0) {
		ff(_x, 0, p[i].si) {
			int x = p[i][_x];
			for(int j = fi[x]; j; j = nt[j]) {
				int y = to[j];
				if(i != id[y]) {
					e[i].pb(id[y]);
				}
			}
		}
	}
	fo(i, 1, id0) {
		ff(_j, 0, e[i].si) {
			int j = e[i][_j];
			r[j] ++;
		}
	}
	fo(i, 1, n) if(!r[i]) {
		d[++ d0] = i;
	}
	for(int i = 1; i <= d0; i ++) {
		int x = d[i];
		ff(_j, 0, e[x].si) {
			int y = e[x][_j];
			if(!(-- r[y])) d[++ d0] = y;
		}
	}
	int ans = 0;
	fd(i, d0, 1) {
		int x = d[i];
		ff(_j, 0, e[x].si) {
			int y = e[x][_j];
			f[x] = max(f[x], f[y]);
		}
		f[x] += v[x];
		ans = max(ans, f[x]);
	}
	pp("%d\n", ans);
}
void tar(int x, int la) {
    d[++ d[0]] = x; low[x] = dfn[x] = ++ td;
    for(int i = final[x]; i; i = next[i]) if(i != (la ^ 1)){
        int y = to[i];
        if(!dfn[y]) tar(y, i), low[x] = min(low[x], low[y]); else
        low[x] = min(low[x], dfn[y]);
    }
    if(dfn[x] == low[x]) {
        tz ++;
        do to[d[d[0]]] = tz; while(d[d[0] --] != x);
    }
}
void tar(int x, int la) {
    dfn[x] = low[x] = ++ tt;
    z[++ z[0]] = x;
    for(int i = e.final[x]; i; i = e.next[i]) {
        int y = e.to[i];
        if(!dfn[y]) {
            tar(y, i);
            low[x] = min(low[x], low[y]);
            if(low[y] >= dfn[x]) {
                td ++;
                while(z[z[0]] != y) e2.link(z[z[0] --], td);
                e2.link(z[z[0] --], td); e2.link(x, td);
            }
        } else if(i != (la ^ 1)) low[x] = min(low[x], dfn[y]);
    }
}
posted @ 2020-08-04 11:52  Cold_Chair  阅读(186)  评论(0编辑  收藏  举报