tarjan求双联通分量--POJ 1523 +P2860 [USACO06JAN]Redundant Paths G
概念:
-
点双联通: 对于一个连通图,如果任意两点至少存在两条点不重复路径,则称这个图为点双连通的(简称双连通)
-
边双联通: 如果任意两点至少存在两条边不重复路径,则称该图为边双连通的
-
点双联通分量: 点双连通的极大子图称为点双连通分量(简称双连通分量)
-
边双联通分量: 边双连通的极大子图称为边双连通分量
性质:
-
每一个点双连通图中,内部无割点,每一个边双连通图中,内部无桥。
-
点双连通图的定义等价于任意两条边都同在一个简单环中,边双连通图的定义等价于任意一条边至少在一个简单环中
-
点双连通分量之间以割点连接,且两个点双连通分量之间有且只有一个割点。
-
每一个割点可任意属于多个点双连通分量,其余点和每条边只属于且属于一个点双连通分量
-
桥不属于任何一个边双连通分量,其余的边和每个顶点都属于且只属于一个边双连通分量
思考: 一个有桥的连通图,如何把它通过加边变成边双连通图? 结论是:叶子数=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;
}