文章总数35祭+tarjan的多种用法:求强连通分量,缩点,割点、桥,LCA,点双连通图、边双联通图、2-SAT问题

文章总数35祭+tarjan的多种用法:求强连通分量,缩点,割点、桥,LCA,点双连通图、边双联通图、2-SAT问题
tarjan算法十分奇妙,有许多不同的运用。下面我们一起看看。

0.背景知识

先度娘一下tarjan:
Robert Tarjan,计算机科学家,以LCA、强连通分量等算法闻名。他拥有丰富的商业工作经验,1985年开始任教于普林斯顿大学。
Robert Tarjan他还在多所大学担任学术职务,如:康奈尔大学(1972-1973年),加州大学伯克利分校(1973-1975),斯坦福大学(1974-1980),纽约大学(1981-1985)。 他也加入过NEC研究所(1989-1997),并在美国麻省理工学院(1996年)担任Visiting Scientist 。
Tarjan:他曾在AT&T贝尔实验室(1980-1989),浩信科技(1997-2001),康柏(2002年)和惠普(2006年至今)工作。 他曾加入ACM和IEEE委员会,并曾为几家期刊的编辑。
Robert Tarjan出生在波莫纳,加利福尼亚州。他的父亲是一个专业儿童精神科医生,以前在国家医院任职。还是孩子的Robert Tarjan就阅读了大量的科学小说,从此对天文学产生兴趣,并梦想成为一名天文学家。他在Scientific American杂志上看完Martin Gardner的数学游戏后又对数学产生了兴趣。他的一位中学老师发现了他对数学的兴趣,从八年级就开始培育他的数学能力。之后Robert开始深入研究数学。
Robert Tarjan上高中就找到了一份工作:从事IBM卡片校对机的工作。 他第一次真正用计算机工作是在1964年,那时他参与Summer Science Program在其中研究天文学。
Robert Tarjan在1969年获得了加州理工学院数学学士学位。在斯坦福大学,他获得了他的计算机科学硕士学位(1971)和博士学位(1972)。在斯坦福,他由罗伯特·弗洛伊德和高德纳指导,两位都是非常突出的计算机科学家。他的博士论文是An Efficient Planarity Algorithm。Robert Tarjan选定计算机科学领域作为他的主要研究方向,是因为他认为计算机科学是实践数学理论的方式,有现实价值。
Robert Tarjan设计了求解的应用领域的许多问题的广泛有效的算法和数据结构。 他已发表了超过228篇理论文章(包括杂志,一些书中的一些章节文章等)。Robert Tarjan以在数据结构和图论上的开创性工作而闻名。 他的一些著名的算法包括 Tarjan最近共同祖先离线算法 ,Tarjan的强连通分量算法 以及Link-Cut-Trees算法等。其中Hopcroft-Tarjan平面嵌入算法是第一个线性时间平面算法。
Tarjan也开创了重要的数据结构如:斐波纳契堆和splay树(splay发明者还有Daniel Sleator)。另一项重大贡献是分析了并查集。他是第一个证明了计算反阿克曼函数的乐观时间复杂度的科学家。
Tarjan与约翰霍普克罗夫特共同于1986年获得图灵奖。
Tarjan还于1994年当选为ACM院士。
Tarjan其他奖项包括:
奈望林纳奖信息科学(1983第一个获奖者)
国家科学院的研究倡议奖 (1984)
巴黎Kanellakis奖-理论与实践( ACM1999)
帕斯卡奖章数学与计算机科学( 欧洲科学院2004)
加州理工学院杰出校友奖( 美国加州技术研究所2010)

好了,BB了那么久,是该讲讲tarjan的前置知识了。

1.DFS
2.没了。

1.求强连通分量

什么叫强连通分量?
如图:

在这里插入图片描述
这整个图都是一个强连通分量
它的定义是:图中任意两个节点可以互相到达
我们借助两个数组:DFN和LOW
DFN的定义是:节点的时间戳(即第几个DFS到)
LOW的定义是:可以回溯的最小的栈中的时间戳
模拟一遍样例:

在这里插入图片描述
然后如何判断一个点是不是在某个强连通分量上的呢?
如果DFN[u]==LOW[u]既是
为什么呢?
我们思考一下两个数组的定义
如果节点u所能回溯的最小DFN值
如果u只能回溯到自己,说明当前栈中起始一段都是带有节点u的强连通分量
因为u不能再回溯了。
来段伪代码压压惊:

void tarjan(int u){
   
	DFN[u]=LOW[u]=++time_line;
	s.push(u);
	for(枚举u的出边){
   
		if(DFN[v]0){
   
			tarjan(v);
			LOW[u]=min(LOW[u],LOW[v]);
		}
		else if(v在栈中){
   
			LOW[u]=min(LOW[u],DFN[v]);
		}
	}
	if(判断u是强连通分量){
   
		while(栈顶元素≠u) 输出栈顶元素,退栈
		输出栈顶元素,退栈
	}
}

这样即可输出当前图中所有的强连通分量
你百度了一圈,也没有发现模板题
因为只用单独输出每个元素即可通过SPJ并AC
. . . . . . ...... ......
所以强连通分量一般与其他题目结合
例题:luogu P2863 [USACO06JAN]牛的舞会The Cow Prom
题目描述
The N (2 <= N <= 10,000) cows are so excited: it’s prom night! They are dressed in their finest gowns, complete with corsages and new shoes. They know that tonight they will each try to perform the Round Dance.
Only cows can perform the Round Dance which requires a set of ropes and a circular stock tank. To begin, the cows line up around a circular stock tank and number themselves in clockwise order consecutively from 1…N. Each cow faces the tank so she can see the other dancers.
They then acquire a total of M (2 <= M <= 50,000) ropes all of which are distributed to the cows who hold them in their hooves. Each cow hopes to be given one or more ropes to hold in both her left and right hooves; some cows might be disappointed.
约翰的N (2 <= N <= 10,000)只奶牛非常兴奋,因为这是舞会之夜!她们穿上礼服和新鞋子,别 上鲜花,她们要表演圆舞.
只有奶牛才能表演这种圆舞.圆舞需要一些绳索和一个圆形的水池.奶牛们围在池边站好, 顺时针顺序由1到N编号.每只奶牛都面对水池,这样她就能看到其他的每一只奶牛.
为了跳这种圆舞,她们找了 M(2<M< 50000)条绳索.若干只奶牛的蹄上握着绳索的一端, 绳索沿顺时针方绕过水池,另一端则捆在另一些奶牛身上.这样,一些奶牛就可以牵引另一些奶 牛.有的奶牛可能握有很多绳索,也有的奶牛可能一条绳索都没有.
对于一只奶牛,比如说贝茜,她的圆舞跳得是否成功,可以这样检验:沿着她牵引的绳索, 找到她牵引的奶牛,再沿着这只奶牛牵引的绳索,又找到一只被牵引的奶牛,如此下去,若最终 能回到贝茜,则她的圆舞跳得成功,因为这一个环上的奶牛可以逆时针牵引而跳起旋转的圆舞. 如果这样的检验无法完成,那她的圆舞是不成功的.
如果两只成功跳圆舞的奶牛有绳索相连,那她们可以同属一个组合.
给出每一条绳索的描述,请找出,成功跳了圆舞的奶牛有多少个组合?
For the Round Dance to succeed for any given cow (say, Bessie), the ropes that she holds must be configured just right. To know if Bessie’s dance is successful, one must examine the set of cows holding the other ends of her ropes (if she has any), along with the cows holding the other ends of any ropes they hold, etc. When Bessie dances clockwise around the tank, she must instantly pull all the other cows in her group around clockwise, too. Likewise,
if she dances the other way, she must instantly pull the entire group counterclockwise (anti-clockwise in British English).
Of course, if the ropes are not properly distributed then a set of cows might not form a proper dance group and thus can not succeed at the Round Dance. One way this happens is when only one rope connects two cows. One cow could pull the other in one direction, but could not pull the other direction (since pushing ropes is well-known to be fruitless). Note that the cows must Dance in lock-step: a dangling cow (perhaps with just one rope) that is eventually pulled along disqualifies a group from properly performing the Round Dance since she is not immediately pulled into lockstep with the rest.
Given the ropes and their distribution to cows, how many groups of cows can properly perform the Round Dance? Note that a set of ropes and cows might wrap many …
input file
Line 1: Two space-separated integers: N and M
Lines 2…M+1: Each line contains two space-separated integers A and B that describe a rope from cow A to cow B in the clockwise direction.
output file
Line 1: A single line with a single integer that is the number of groups successfully dancing the Round Dance.
一句话题意:输出元素个数 ≥ 2 ≥2 2的元素个数
近乎模板题
只用把输出强连通分量改为统计答案+求和即可
代码:

#include<bits/stdc++.h>
using namespace std;
const int N=200005;
int n,m;
int hed[N],tal[N],nxt[N],cnt=0;
int dfn[N]={
   0};//时间戳 
int low[N];//能返回的最小的栈中时间戳 
int indexy=0;//当前时间戳 
int ans=0;//答案 
stack<int> s;//栈 
bool vis[N]={
   0};//是否在栈中 
void add(int x,int y){
   
 	cnt++;
 	tal[cnt]=y;
 	nxt[cnt]=hed[x];
	 hed[x]=cnt;
}
void tarjan(int x){
   
	 low[x]=dfn[x]=++indexy;
	 s.push(x);
	 vis[x]=1;
	 for(int i=hed[x];i;i=nxt[i]){
   
  		int y=tal[i];
  		if(!dfn[y]){
   
  			 tarjan(y);
   			 low[x]=min(low[x],low[y]);
	     }
  		else if(vis[y]){
   
   			low[x]=min(low[x],dfn[y]);
  		}
 	} 
 	if(low[x]==dfn[x]){
   //找到一个强连通分量 
  		int sum=1;
  		vis[x]=0;
  		while(s.top()!=x){
   
   			sum++;
   			vis[s.top()]=0;
  			 s.pop();
  		} 
  		s.pop();
 		 if(sum>1) ans++;
 	}
}
int main(){
   
 	scanf("%d%d",&n,&m); 
 	while(m--){
   
  		int x,y;
 	 	scanf("%d%d",&x,&y);
 	 	add(x,y);
	}
 	for(int i=1;i<=n;i++){
   
  		if(!dfn[i]){
   
   			tarjan(i);
  		}
 	}
 	printf("%d\n",ans);
 	return 0;
}

你以为强连通分量就这样结束了?

真的就这样结束了?

真的?

NO!

(敲黑板)你是否注意到

else if(vis[y]){
   
      low[x]=min(low[x],dfn[y]);
}

为啥不是

else if(vis[y]){
   
      low[x]=min(low[x],low[y]);
}

呢?
事实上, 99.999999 99.999999 99.999999%的强连通分量都可以用第二种写法水过
(几乎是全部的)
(话说输出所有元素可以通过100%呢!)
但是

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值