嗯
开新坑了——圆方树
圆方树
先来讲讲圆方树是个啥。
相对于tarjan对于有向有环图进行的缩点,圆方树在tarjan的基础上进行了进一步的拓展.
他能在无向图上进行,而对于有向图的强联通分量,则变为了无向图中的边双或点双。
而功能也是更加的强大,无向图->树。
但是也是对应能解决一部分的问题。
比如圆方树的板子题:求任意两点之间一定要经过的点的个数。
一定要经过的点,即为割点。
对于一个无向图,一个没有割点部分是点双(但是这并不严谨,点双的定义为,点双所涵盖的图中,任意两点之间有两条以上的简单路径相连(个人的理解))
点(边)双:若一个无向图中的去掉任意一个节点(一条边)都不会改变此图的连通性,即不存在割点(桥),则称作点(边)双连通图;(来自度娘)
其实对于上面的圆方树板子题,一定要经过的点就是割点,因为除了这个点,我们是不能再找到另一个路径前往下一个边双的,不然他就不是割点了(删除它以后,对图的连通性没有影响)。
其实圆方树原本是来解决仙人掌的问题的。因为对于仙人掌的每一个环,都是一个点双。。。
所以圆方树如何实现呢?
看这样的图
对于第一个无向图,可以发现它有4个点双。
在第二个图中,有4个方点,每一个对应了一个点双。
如图,我们把每一个点双中的点连接到在新图中对应的方点上。
这就变成了一个树。
一个方点有且只有一个点双对应。但是一个点可能连接很多个方点,那么就说明这是个割点。
那么怎么来求点双呢?
tarjan在无向图中的应用之一就是求割点。
其实也可以不用圆方树,可是圆方树好就好在建图极为的方便,和tarjan几乎是绝配,因为方点其实就是染色,所以在tarjan中就可以直接进行建树了,和有向图缩点的方向其实差不多
建树具体方案如下:
1. 根据tarjan算法,求出每个点双。
2. 拆掉每个点双内部的所有边。
3. 将这个点双对应的方点向这个点双内的每一个圆点连一条边。
这样每一个点双就成为了一个树的形态,这样所有的都连起来也就是一颗树了。
建树 代码
#include<bits/stdc++.h>
const int MN = 100005;
int N, M, cnt; vector<int> G[MN], T[MN * 2]; //G是原图,T是圆方树 int dfn[MN], low[MN], dfc; int stk[MN], tp; void Tarjan(int u) { printf(" Enter : #%d\n", u); low[u] = dfn[u] = ++dfc; // low 初始化为当前节点 dfn stk[++tp] = u; // 加入栈中 for (int i=0;i<G[u].size();i++) { // 遍历 u 的相邻节点 int v=G[u][i]; if (!dfn[v]) { // 如果未访问过 Tarjan(v); // 递归 low[u] = std::min(low[u], low[v]); // 未访问的和 low 取 min if (low[v] >= dfn[u]) { // 标志着找到一个以 u 为根的点双连通分量 ++cnt; // 增加方点个数 printf(" Found a New BCC #%d.\n", cnt - N); // 将点双中除了 u 的点退栈,并在圆方树中连边 for (int x = 0; x != v; --tp) { //!!!! x = stk[tp]; T[cnt].push_back(x); T[x].push_back(cnt); printf(" BCC #%d has vertex #%d\n", cnt - N, x) } // 注意 u 自身也要连边(但不退栈) T[cnt].push_back(u); T[u].push_back(cnt); printf(" BCC #%d has vertex #%d\n", cnt - N, u); } } else low[u] = std::min(low[u], dfn[v]); // 已访问的和 dfn 取 min } printf(" Exit : #%d : low = %d\n", u, low[u]); printf(" Stack:\n "); for (int i = 1; i <= tp; ++i) printf("%d, ", stk[i]); puts(""); } int main() { scanf("%d%d", &N, &M); cnt = N; // 点双 / 方点标号从 N 开始 for (int i = 1; i <= M; ++i) { int u, v; scanf("%d%d", &u, &v); G[u].push_back(v); // 加双向边 G[v].push_back(u); } // 处理非连通图 for (int u = 1; u <= N; ++u) if (!dfn[u]) Tarjan(u), --tp; // 注意到退出 Tarjan 时栈中还有一个元素即根,将其退栈 return 0;
}
这就是部分代码了。
嗯
对于我说的那个末班题嘛,你还需要一个lca,可供选择的有:倍增,树链,balalbala,反正我jio得还是倍增好写 虽然慢
嗯
码量巨大awa
#include<bits/stdc++.h>
#define ll long long
using namespace std;
struct yuanx
{
ll next,to;
}tree[2000001];
struct edge
{
ll next,to,v;
}e[2000001];
ll head[2000001],head2[2000001],tot2,tot,n,m,Q,dep[2000001];
ll f[2000001][23],fa[2000001],dfn[2000010],low[2000001],ccs;
ll dfsc,cnt,xx1[2000001],yy1[2000001];
bool vis[2000001],vis2[2000001];
inline ll read()
{
char c=getchar();ll a=0,b=1;
for(;c<'0'||c>'9';c=getchar())if(c=='-')b=-1;
for(;c>='0'&&c<='9';c=getchar())a=a*10+c-48;
return a*b;
}
void add(ll i,ll j)
{
e[++tot].next=head[i];
e[tot].to=j;
head[i]=tot;
}
void add2(ll i,ll j)
{
tree[++tot2].next=head2[i];
tree[tot2].to=j;
head2[i]=tot2;
}
void dfs(ll x,ll faa)
{
dep[x]=dep[faa]+1;
for(ll i=1;(1<<i)<=dep[x];i++)
{
f[x][i]=f[f[x][i-1]][i-1];
}
for(ll i=head2[x];i!=0;i=tree[i].next)
{
ll u=tree[i].to;
if(u==faa)continue;
f[u][0]=x;
dfs(u,x);
}
}
ll lca(ll x,ll y)
{
if(dep[x]<dep[y])swap(x,y);
for(ll i=22;i>=0;i--)
{
if(dep[f[x][i]]>=dep[y])
{
x=f[x][i];
}
if(x==y)
{
return x;
}
}
if(x==y)
{
return x;
}
for(ll i=20;i>=0;i--)
{
if(f[x][i]!=f[y][i])
{
x=f[x][i];y=f[y][i];
}
}
return f[x][0];
}
int s[2000001],top;
void tarjan(ll x)
{
dfn[x]=low[x]=++dfsc;
s[++top]=x;vis[x]=true;
for(ll i=head[x];i!=0;i=e[i].next)
{
ll u=e[i].to;
if(dfn[u]==0)
{
tarjan(u);
low[x]=min(low[x],low[u]);
if(low[u]>=dfn[x])
{
cnt++;
for(int v=0;v!=u;top--)
{
v=s[top];
add2(v,cnt);
add2(cnt,v);
}
add2(cnt,x);add2(x,cnt);
}
}
else
if(vis[u]==true)
{
low[x]=min(low[x],dfn[u]);
}
}
}
ll lcaa(ll x,ll y)
{
return (dep[x]+dep[y]-dep[lca(x,y)]*2-2)/2;
}
int main()
{
n=read();m=read();
cnt=n;
for(ll i=1;i<=m;i++)
{
ll x=read();ll y=read();
add(x,y);add(y,x);
xx1[i]=x;yy1[i]=y;
}
for(ll i=1;i<=n;i++)
{
if(!dfn[i])
{
tarjan(i);
top=0;
}
}
dfs(1,1);
Q=read();
for(ll i=1;i<=Q;i++)
{
ll x=read();ll y=read();
ll ans=0;
ans=max(ans,lcaa(xx1[x],xx1[y]));
ans=max(ans,lcaa(xx1[x],yy1[y]));
ans=max(ans,lcaa(yy1[x],xx1[y]));
ans=max(ans,lcaa(yy1[x],yy1[y]));
cout<<ans<<endl;
}
return 0;
}
代码巨丑无比(逃
来源:oschina
链接:https://my.oschina.net/u/4278528/blog/4299753