圆方树是用于处理无向图上的一类问题,将图的点双连通分量作为一个方点,将这张图变为一棵树,一般观察到题目的要求中点双上具有一些性质时常常可以考虑使用圆方树将图转化为树。
在圆方树中,原来的每个点对应一个圆点,每一个点双对应一个方点。所以共有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;
}