圆方树

圆方树是用于处理无向图上的一类问题,将图的点双连通分量作为一个方点,将这张图变为一棵树,一般观察到题目的要求中点双上具有一些性质时常常可以考虑使用圆方树将图转化为树。
在圆方树中,原来的每个点对应一个圆点,每一个点双对应一个方点。所以共有n+c个点,其中n是原图点数,c是原图点双连通分量的个数。而对于每一个点双连通分量,它对应的方点向这个点双连通分量中的每个点连边。每个点双形成一个“菊花图”,多个“菊花图”通过原图中的割点连接在一起(因为点双的分隔点是割点)。
圆方树的性质:
1.树上只存在“圆圆边”和“圆方边”。
2.每一个方点对应一个点双联通分量。
3.方点的点度是点双联通分量的大小。
4.圆点为割点时才有超过1个儿子。(不是割点的仅连着一个方点)
记得空间一定要开两倍。
圆方树的构建:

#include <iostream>
#include <vector>
using namespace std;

typedef long long ll;

const int maxn = 1e5+5;

vector<int> g[maxn],tree[maxn*2];  //原图与点方树 
int cnt,n;
int dfn[maxn],low[maxn],dfc;  
//dfn为i点的dfs序,low存储的是节点i的dfs树中的子树中的某个点t通过最多一次返祖边或向父亲的树边能访问到的点的最小的dfs序.
int sta[maxn],tp = 0;   //维护一个栈 

void tarjan(int x)
{
	dfn[x] = low[x] = ++dfc;   //标记dfs序 
	sta[++tp] = x;     //入栈 
	for (int i = 0; i < g[x].size(); i++)
	{
		int t = g[x][i];
		if( !dfn[t] )
		{
			tarjan(t);
			low[x] = min(low[x],low[t]);  //t能访问到的最小的dfs序,x也一定能访问到 
			if( low[t] == dfn[x] )     //low[t]==dfn[x]时说明x是这个点双中深度最浅的点 
			{
				++cnt;
				for (int z = 0; z != t; --tp)  //把子树中所有未确定的点拿出来 
				{
					z = sta[tp];
					tree[cnt].push_back(z);   //全部与新的方点相连,即构成了一个新的点双 
					tree[z].push_back(cnt);
				}
				tree[cnt].push_back(x);   
				//包括这个x点,也属于这个点双,但是这个点也可能属于别的点双,所以没有出栈 
				tree[x].push_back(cnt);
			}
		}else low[x] = min(low[x],dfn[t]);  //尝试通过返祖边 
	}
}

int main()
{
	ios::sync_with_stdio(false);
	cin.tie(0);cout.tie(0);
	int m;
	cin >> n >> m;
	cnt = n;
	for (int i = 1; i <= m; i++)
	{
		int x,y;
		cin >> x >> y;
		g[x].push_back(y);
		g[y].push_back(x);
	} 
	for (int i = 1; i <= n; i++)
	{
		if( !dfn[i] ) tarjan(i),tp --;   //注意这里还有一个点未出栈 
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值