zoukankan      html  css  js  c++  java
  • Tarjan

    Tarjan

    割点 & 割边(桥)

    先引入两个概念:时间戳追溯值

    时间戳

    在 $ dfs $ 遍历图时,每个节点第一次被访问的顺序,就是这个点的 时间戳,通常用 (dfn[u]) 表示;

    追溯值

    节点 $ u $ 的追溯值 定义为以下节点的 时间戳 的最小值;

    1.$ subtree(u) $ 中的节点;

    2.通过 1 条不在搜索树上的边,能够到达 $ subtree(u) $ 的节点;

    通常用 $ low[u] $ 表示;

    割边

    无向连通图中,去掉一条边,图中的连通分量数增加,则这条边,称为桥或者割边,当然也是在无向图。

    无向边 $ (x,y) $是桥, 当且仅当搜索树上存在 $ x $ 的一个节点 $ y $ ,满足: $ dfn[x] < low[y] $

    需要注意的是,因为我们遍历的是无向图,所以从每个点 $ x $ 出发,总能访问到它的父节点 $ fa $ ,根据 $ low $ 的计算方法,$ (x,fa) $ 属于搜索树上的边

    且 $ fa $ 不是 $ x $ 的子节点,所以不能用 $ fa $ 的时间戳来更新 $ low[x] $ ;

    但是如果有重边的情况,那么 $ fa $ 与 $ x $ 之间不在搜索树上的边是可以更新 $ low[x] $ 的;

    所以我们需要知道 $ fa $ 与 $ x $ 之间的边是不是搜索树上的边,那么我们可以利用成对变换技巧就可以了;

    void tarjan(int u,int in){
    	dfn[u]=low[u]=++tot;
    	for(int i=head[u];i;i=e[i].nxt){
    		int to=e[i].v;
    		if(!dfn[to]){ //是否是子树上的点
    			tarjan(to,i);
    			low[u]=min(low[u],low[to]);
    			if(dfn[u]<low[to]){
                    //.......
    			}
    		}else if(i!=(in^1)){ //判断是否是搜索树上的边
    			low[u]=min(low[u],dfn[to]);
    		}
    	}
    }
    

    割点

    如果在一个图中,如果把一个点删除,那么这个图不再连通,那么这个点就是割点(割顶),当然是在无向图。

    若 $ x $ 不是搜索树的根节点,则 $ x $ 是割点当且仅当搜索树上存在 $ x $ 的一个子节点 $ y $ ,满足:$ dfn[x] <= low[y] $

    若 $ x $ 是搜索树的根节点,则 $ x $ 是割点当且仅当搜索树上存在至少两个子节点 $ y1,y2 $ ,满足以上条件;

    可以发现父节点和重边的情况是不必考虑的 ,想想为什么

    void tarjan(int u){
    	dfn[u]=low[u]=++tot;
    	int flag=0;
    	for(int i=head[u];i;i=e[i].nxt){
    		int to=e[i].v;
    		if(!dfn[to]){
    			tarjan(to);
    			low[u]=min(low[u],low[to]);
    			if(dfn[u]<=low[to]){
    				flag++;
    				if(u!=1||flag>1) cut[u]=1;// 根节点特殊的情况
    			}
    		}else low[u]=min(low[u],dfn[to]);
    	}
    }
    

    P3388 【模板】割点(割顶)

    P3225 [HNOI2012]矿场搭建

    模板:割点和桥

    边双连通分量

    在一张连通的无向图中,对于两个点 $u $ 和 $ v $ ,如果无论删去哪条边(只能删去一条)都不能使它们不连通,我们就说 (u)(v) 边双连通

    边双连通的极大子图称为边双连通分量

    任意一条边至少在一个简单环中

    边双连通分量怎么求?

    先求出那些边是桥,再 $ dfs $ 就可以了

    #include<iostream>
    #include<cstdio>
    using namespace std;
    const int N=1e5+7;
    const int M=3e5+7;
    struct edge{
    	int v,nxt;
    }e[M<<2];
    int n,m,tot,cnt=1,ans;
    int head[N],dfn[N],low[N],bri[N],vis[10000000];
    void add_edge(int u,int v){
    	e[++cnt]=(edge){v,head[u]};
    	head[u]=cnt;
    }
    
    void tarjan(int u,int in){
    	dfn[u]=low[u]=++tot;
    	for(int i=head[u];i;i=e[i].nxt){
    		int to=e[i].v;
    		if(!dfn[to]){
    			tarjan(to,i);
    			low[u]=min(low[u],low[to]);
    			if(dfn[u]<low[to]){
    				bri[i]=bri[i^1]=1;
    			}
    		}else if(i!=(in^1)){
    			low[u]=min(low[u],dfn[to]);
    		}
    	}
    }
    
    void dfs(int u,int fa){
    	vis[u]=fa;
    	for(int i=head[u];i;i=e[i].nxt){
    		int to=e[i].v;
    		if(vis[to]||bri[i]) continue;
    		dfs(to,fa);
    	}
    }
    
    int main(){
    	scanf("%d%d",&n,&m);
    	for(int i=1;i<=m;i++){
    		int x,y;
    		scanf("%d%d",&x,&y);
    		add_edge(x,y);
    		add_edge(y,x); 
    	}
    	for(int i=1;i<=n;i++) if(!dfn[i]) tarjan(i,0);
    	for(int i=1;i<=n;i++){
    		if(!vis[i]){
    			dfs(i,++ans);
    		}
    	}
    	cout<<ans; 
    }
    

    点双连通分量

    点双连通分量是一个极大的子图,满足图中删去任何一个点都不会改变图的连通性。

    也就是不存在割点

    性质

    1.点双连通分量中没有割点。

    2.若干个点双连通分量以割点相连接。

    3.原图中的每个割点可能属于多个点双连通分量,其它点只可能属于一个点双连通分量。所以出栈时不能全部出栈

    4.任意两点至少存在两条边不重复路径.

    求法

    1.当一个节点第一次被访问时,将该点入栈。

    2.当找到割点时,无论 $ u $ 是否为根,从栈中不断弹出节点,知道 $ to $ 节点被弹出;

    3.弹出的节点与 $ u $ 构成一个 $ v - DCC $ 。

    看代码

    void tarjan(int u){
    	dfn[u]=low[u]=++tot;
    	if(u==rt&&head[u]==0){
    		dcc[++cnt].push_back(u); //孤立点
    		return;
    	}
    	st[++h]=u; //入栈
    	for(int i=head[u];i;i=e[i].nxt){
    		int to=e[i].v;
    		if(!dfn[to]){
    			tarjan(to);
    			low[u]=min(low[u],low[to]);
    			if(dfn[u]<=low[to]){ //是割点
    				cnt++;
    				int k;
    				do{
    					k=st[h];
    					h--;
    					dcc[cnt].push_back(k);
    				}while(k!=to); // 注意这里是to,不是u,因为割点可能在多个 v-DCC 中
    				dcc[cnt].push_back(u);
    			}
    		}else low[u]=min(low[u],dfn[to]);
    	}
    }
    

    UVA1364 Knights of the Round Table

    圆方树

    在圆方树中,原来的每个点对应一个圆点,每一个点双对应一个方点
    所以共有 $ n+c $ 个点,其中 $ n $ 是原图点数,$ c $ 是原图点双连通分量的个数。

    而对于每一个点双连通分量,它对应的方点向这个点双连通分量中的每个点连边。
    每个点双形成一个“菊花图”,多个“菊花图”通过原图中的割点连接在一起(因为点双的分隔点是割点)。

    显然,圆方树中每条边连接一个圆点和一个方点。

    圆方树的点数小于 $ 2n $,这是因为割点的数量小于 $ n $,所以请注意各种数组大小要开两倍。

    其实,如果原图连通,则“圆方树”才是一棵树,如果原图有 $ k $ 个连通分量,则它的圆方树也会形成 $ k $ 棵树形成的森林。

    就把图上问题转化成了树上问题,那怎么构建圆方树呢,重新建边就好了,见代码;

    void tarjan(int u){
    	dfn[u]=low[u]=++tot;
    	st[++h]=u;
    	num++;
    	for(int i=head[u];i;i=e[i].nxt){
    		int to=e[i].v;
    		if(!dfn[to]){
    			tarjan(to);
    			low[u]=min(low[u],low[to]);
    			if(dfn[u]<=low[to]){
    				int k;
    				++n;
    				w[n]++;
    				add(u,n); // 重新建边
    				add(n,u);
    				do{
    					k=st[h];
    					h--;
    					w[n]++;
    					add(k,n); 
    					add(n,k); 
    				}while(to!=k);
    			}
    		}else low[u]=min(low[u],dfn[to]);
    	}
    }
    

    P4630 [APIO2018] Duathlon 铁人两项

    P4606 [SDOI2018]战略游戏

    有向图的强连通分量

    有向图 G 强连通是指,G 中任意两个结点连通。强连通分量(Strongly Connected Components,SCC)的定义是:极大的强连通子图。

    tarjan 学习网址

    $ dfn $ : 在 $ dfs $ 遍历图时,每个节点第一次被访问的顺序,就是这个点的 时间戳

    追溯值

    节点 $ u $ 的追溯值 定义为以下节点的 时间戳 的最小值;

    1.栈中的节点;

    2.存在一条从子树出发的有向边,以改点为终点;

    可以发现,$ dfn[u] $ 必然大于等于 $ low[u] $ ,当 $ dfn[u] = low[u] $ 时 说明这个节点是当前这个强联通量的最小的 $ dfn $ ,就可以弹栈了;

    void tarjan(int now){
    	dfn[now]=low[now]=++cnt;
    	st[++h]=now;
    	vis[now]=1; //判断是否在栈中
    	for(int i=head[now];i;i=e[i].nxt){
    		int to=e[i].v;
    		if(!dfn[to]){
    			tarjan(to);
    			low[now]=min(low[now],low[to]);
    		}else if(vis[to]){
    			low[now]=min(low[now],dfn[to]);
    		}
    	}
    	int k;
    	if(dfn[now]==low[now]){
    		++cur;
    		do{
    			k=st[h],h--;
    			vis[k]=0;
    			id[k]=cur,sum[cur]+=a[k];
    		}while(now!=k);
    	}
    }
    

    P3119 [USACO15JAN]Grass Cownoisseur G

    P5008 [yLOI2018] 锦鲤抄

  • 相关阅读:
    poj 1579(动态规划初探之记忆化搜索)
    hdu 1133(卡特兰数变形)
    CodeForces 625A Guest From the Past
    CodeForces 625D Finals in arithmetic
    CDOJ 1268 Open the lightings
    HDU 4008 Parent and son
    HDU 4044 GeoDefense
    HDU 4169 UVALive 5741 Wealthy Family
    HDU 3452 Bonsai
    HDU 3586 Information Disturbing
  • 原文地址:https://www.cnblogs.com/Aswert/p/14273854.html
Copyright © 2011-2022 走看看