Tarjan大杂烩(也不算太大……)

11 篇文章 0 订阅


Tarjan求LCA

用Tarjan求LCA可以用 O ( N l o g N + Q ) O(N log N+Q) O(NlogN+Q)的时间复杂度离线求出 Q Q Q个LCA询问哟!

思路:

我们先对树进行遍历。众所周知,A若是在B的子树里,那A和B的LCA就是B。然后第二种情况,设一个k,A和B在k的子树里,且k是深度最大的,那k就是A和B的LCA。
这是基本的思路,那怎样实现呢?
我们想到了并查集。
每次遍历,用并查集将每个点的父亲记录下来,每次回溯前将要求的LCA遍历(当然vector是更好的选择),通过并查集来找出两点的LCA

c o d e code code

void dfs(int x)
{
	f[x]=x;
	b[x]=1;
	for(int i=head[x]; i; i=e[i].next)
	{
		int to=e[i].to;
		if(!b[to])
			dfs(to), f[to]=x;
	}
	for(int u=1; u<=n; u++)
		if(a[x][u]&&b[u])
			lca[x][u]=find_fa(x);
	return;
}

Tarjan求割点、割边

割点就是一个连通图中把它和它相连的边删掉以后变得不连通的点。
至于用Tarjan求割点,和Tarjan缩点的思路相似。

c o d e code code

void tarjan(int x)
{
	fn[x]=low[x]=++cnt;
	v[x]=1;
	int child=0;
	for(int i=head[x]; i; i=b[i].next)
	{
		int y=b[i].to;
		if(!dfn[y])
		{
			if(dfn[x]==1)
			{
				child++;
				if(child>=2)
					key[x]=1;
			}
			tarjan(y);
			low[x]=min(low[x], low[y]);
			if(dfn[x]<=low[y]&&dfn[x]>1)
				key[x]=1;
		}
		else
			low[x]=min(low[x], dfn[y]);
	}
} 

至于求割边(定义差不多),思路和代码基本完全一致

c o d e code code

void tarjan(int x)
{
	fn[x]=low[x]=++cnt;
	v[x]=1;
	int child=0;
	for(int i=head[x]; i; i=b[i].next)
	{
		int y=b[i].to;
		if(!dfn[y])
		{
			tarjan(y);
			low[x]=min(low[x], low[y]);
			if(dfn[x]<low[y])
				key[x][y]=1;
		}
		else
			low[x]=min(low[x], dfn[y]);
	}
} 

Tarjan求强连通分量

总之就是那个缩点

c o d e code code

void tarjan(int x)
{
	dfn[x]=low[x]=++cnt;
	v[x]=1;
	stack[++top]=x;
	for(int i=head[x]; i; i=b[i].next)
	{
		int y=b[i].to;
		if(!dfn[y])
		{
			tarjan(y);
			low[x]=min(low[x], low[y]);
		}
		else if(v[y])
			low[x]=min(low[x], dfn[y]);
	}
	if(dfn[x]==low[x])
	{
		tmp++;
		do
		{
			c[stack[top]]=tmp;
			//cout<<c[stack[top]]<<' '<<stack[top]<<endl;
			d[tmp]+=a[stack[top]];
			v[stack[top]]=0;
			top--;
		}
		while(x!=stack[top+1]);
	}
	return;
}

总结

又水了一篇……

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值