title : 圆方树
date : 2021-10-22
tags : ACM,图论
author : Linno
圆方树
是一种将一般图变为树的方法,进而将一般图上的某些问题转化为树上问题。
基本概念
仙人掌图:不存在边同时属于多个环的无向连通图
对于每个环新建一个方点,该方点想换上每一个圆点连边,就构成了圆方树。
点双连通图:图中任意两不同点之间都有至少两条点不重复的路径。
双连通分量:一个图的点双连通分量是一个极大点双连通子图。
在圆方树中,每个点双对应一个方点,原图每个点对应一个圆点,
构建方法
用tarjan算法求出点双连通分量,对于每一个点双连通分量新建一个方点与环上的点相连(注意一条边不属于点双)
void tarjan(int x){ //tarjan求双连通分量
dfn[x]=low[x]=++idx;
stk[++top]=x;
for(int i=G.head[x];i;i=G.nxt[i]){
int to=G.to[i];
if(!dfn[to]){
tarjan(to);
low[x]=min(low[x],low[to]);
if(low[to]==dfn[x]){ //建立圆方树
T.addedge(++tot,x);
int tmp=0;
do{
tmp=stk[top--];
T.addedge(tot,tmp);
}while(tmp!=to);
}
}else low[x]=min(low[x],dfn[to]);
}
}
广义圆方树
普通圆方树只能解决仙人掌图上的问题,而广义圆方树可以将所有无向图转化为圆方树处理。
性质
圆点方点之间,不存在两个“形状相同”的点相连。
以r为根的仙人掌上p的子仙人掌就是圆方树中以r为根时,p子树中的所有圆点。
void tarjan(int x,int f){
dfn[x]=low[x]=++tim;
st[++tot]=x;
for(int i=G.head[x];i;i=G.nex[i])
if(G.v[i]!=f){
int y=G.v[i];
if(!dfn[y]){
tarjan(y,x);
low[x]=min(low[x],low[y]);
if(low[y]>=dfn[x]){
siz++;
while(st[tot]!=y)
T.add(n+siz,st[tot--]);
T.add(n+siz,st[tot--]);
T.add(n+siz,x);
}
}
else
low[x]=min(low[x],dfn[y]);
}
}
例题
luoguP4320 道路相遇
#include<bits/stdc++.h>
#define inf 0x3f3f3f3f
#define int long long
using namespace std;
const int maxn=1e6+7;
const int mod=1e9+7;
int read(){ int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-') f=f*-1;ch=getchar();}while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}return x*f;}
int n,m,q,u,v,w,tot;
int dfn[maxn],low[maxn],idx;
int stk[maxn],top;
int dep[maxn],sz[maxn],tp[maxn],fa[maxn],son[maxn];
struct Graph{
int head[maxn],cnt,nxt[maxn<<2],to[maxn<<2];
inline void addedge(int u,int v){
nxt[++cnt]=head[u];to[cnt]=v;head[u]=cnt;
nxt[++cnt]=head[v];to[cnt]=u;head[v]=cnt;
}
}G,T; //原图以及圆方树
void dfs1(int x){
sz[x]=1;
for(int i=T.head[x];i;i=T.nxt[i]){
int to=T.to[i];
if(fa[x]==to) continue;
dep[to]=dep[x]+1; //处理深度
fa[to]=x; //处理父亲信息
dfs1(to);
sz[x]+=sz[to]; //处理子树大小
if(sz[to]>sz[son[x]]) son[x]=to; //重儿子
}
}
void dfs2(int x,int top){
tp[x]=top; //top为起点
if(son[x]) dfs2(son[x],top); //重链
for(int i&#