tarjan求双联通分量--POJ 1523 +P2860 [USACO06JAN]Redundant Paths G

概念:

  1. 点双联通: 对于一个连通图,如果任意两点至少存在两条点不重复路径,则称这个图为点双连通的(简称双连通)

  2. 边双联通: 如果任意两点至少存在两条边不重复路径,则称该图为边双连通的

  3. 点双联通分量: 点双连通的极大子图称为点双连通分量(简称双连通分量)

  4. 边双联通分量: 边双连通的极大子图称为边双连通分量

性质:

  1. 每一个点双连通图中,内部无割点,每一个边双连通图中,内部无桥。

  2. 点双连通图的定义等价于任意两条边都同在一个简单环中,边双连通图的定义等价于任意一条边至少在一个简单环中

  3. 点双连通分量之间以割点连接,且两个点双连通分量之间有且只有一个割点。

  4. 每一个割点可任意属于多个点双连通分量,其余点和每条边只属于且属于一个点双连通分量

  5. 桥不属于任何一个边双连通分量,其余的边和每个顶点都属于且只属于一个边双连通分量

思考: 一个有桥的连通图,如何把它通过加边变成边双连通图? 结论是:叶子数=1 答案为 0 否则为(叶子数+1)/2

例题:

因为只有割点会包含在多个点双联通分量里,所以我们记录每个点包含在多少个点双联通分量里,其中数量>1的就是割点,且等于删掉后形成的联通块个数

注意: 弹栈的时候不能把割点弹掉,因为我其他点双联通分量里也许会也会有这个割点

code:

#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
using namespace std;
int read(){
	int x = 1,a = 0;char ch = getchar();
	while (ch < '0'||ch > '9'){if (ch == '-') x = -1;ch = getchar();}
	while (ch >= '0'&&ch <= '9'){a = a*10+ch-'0';ch = getchar();}
	return x*a;
}
const int maxn = 2e3+10;
int n,m;
struct node{
	int to,next;
}ed[maxn*2];
int head[maxn*2],tot;
void add(int u,int to){
	ed[++tot].to = to;
	ed[tot].next = head[u];
	head[u] = tot;
}
int dfn[maxn],low[maxn],cnt,flag;
int top,st[maxn],bcc,vis[maxn];
void tarjan(int x,int fa){
    low[x] = dfn[x] = ++cnt;
    st[++top] = x;
    if (x == fa&&head[x] == 0){vis[x]++;return;}
    for(int i = head[x];i;i = ed[i].next){
        int to = ed[i].to;
        if(!dfn[to]){
            tarjan(to,fa);
            low[x] = min(low[x],low[to]);
            if(low[to] >= dfn[x]){
            	bcc++;top++;
                int y;
                while (y = st[--top]){
                	vis[y]++;
                	if (x == y) break;
                }
            }
        }
        else low[x] = min(low[x],dfn[to]);
    }
}
void init(){
	memset(head,0,sizeof(head));
	memset(ed,0,sizeof(ed));
	memset(vis,0,sizeof(vis));
	memset(low,0,sizeof(low));
	memset(dfn,0,sizeof(dfn));
	top = 0,cnt = 0,tot = 0,bcc = 0,flag = 0;
}
int num;
int main(){
	int x,y;
	while (~scanf ("%d",&x)&&x != 0){
		num++;
		printf("Network #%d\n",num);
		init();
		int maxx = 0;
		y = read();
		if (x != y) add(x,y),add(y,x);
		maxx = max(maxx,max(x,y));
		while (~scanf ("%d",&x)&&x != 0){
			y = read();
			if (x == y) continue;
			add(x,y),add(y,x);
			maxx = max(maxx,max(x,y));
		}
		for (int i = 1;i <= maxx;i++){
			if (!dfn[i]) tarjan(i,i);
		}
		for (int i = 1;i <= maxx;i++){
			if (vis[i] > 1){printf("  SPF node %d leaves %d subnets\n",i,vis[i]);flag = 1;}
		}
		if (!flag) printf("  No SPF nodes\n");
		puts("");
	}
	return 0;
}

第二题就可以直接用我们上面思考的结论

code:

#include <iostream>
#include <algorithm>
#include <cstdio>
using namespace std;
int read(){
	int x = 1,a = 0;char ch = getchar();
	while (ch < '0'||ch > '9'){if (ch == '-') x = -1;ch = getchar();}
	while (ch >= '0'&&ch <= '9'){a = a*10+ch-'0';ch = getchar();}
	return x*a;
}
const int maxn = 3e6+10;
int n,m;
struct node{
	int to,next;
}ed[maxn*2];
int head[maxn*2],tot = 1;
void add(int u,int to){
	ed[++tot].to = to;
	ed[tot].next = head[u];
	head[u] = tot;
}
int dfn[maxn],low[maxn],cnt,res;
int bridge[maxn];
void tarjan(int x, int fa) {
    dfn[x] = low[x] = ++cnt;
    for (int i = head[x];i;i = ed[i].next) {
        int to = ed[i].to;
        if (!dfn[to]) {
            tarjan(to, i);
            low[x] = min(low[x],low[to]);
            if (low[to] > dfn[x])
                bridge[i] = bridge[i^1] = true;
        }
        else if (i != (fa^1)) low[x] = min(low[x],dfn[to]);
    }
}
int col[maxn],dcc=0;
void dfs(int x,int color){
    col[x]=color;
    for(int i = head[x];i;i = ed[i].next){
            int to = ed[i].to;
            if(bridge[i])continue;
            if(!col[to]) dfs(to,color);
    }
}
int du[maxn];
int main(){
	n = read(),m = read();
	for (int i = 1;i <= m;i++){
		int u = read(),v = read();
		add(u,v),add(v,u);
	}
	for (int i = 1;i <= n;i++){
		if (!dfn[i]) tarjan(i,0);
	}
	int res = 0;
    for(int i = 1;i <= n;i++)
        if(!col[i]) dfs(i,++dcc);
    for (int i = 1;i <= n;i++){
		for (int j = head[i];j;j = ed[j].next){
			int to = ed[j].to;
		//	printf("col[%d] = %d col[%d] = %d\n",i,col[i],to,col[to]);
			if (col[to] != col[i]) du[col[to]]++,du[col[i]]++;
			//printf("du[%d] = %d du[%d] = %d\n",col[i],du[col[i]],col[to],du[col[to]]);
		}
	}
	int num = 0;
	for (int i = 1;i <= dcc;i++)
		if (du[i] == 2) num++;
	printf("%d\n",(num+1)/2);
	return 0;
}
posted @ 2020-11-10 17:13  小又又yyyy  阅读(66)  评论(0编辑  收藏  举报