嗯
开新坑了——圆方树
圆方树
先来讲讲圆方树是个啥。
相对于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 ", 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. ", 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 ", cnt - N, x) } // 注意 u 自身也要连边(但不退栈) T[cnt].push_back(u); T[u].push_back(cnt); printf(" BCC #%d has vertex #%d ", cnt - N, u); } } else low[u] = std::min(low[u], dfn[v]); // 已访问的和 dfn 取 min } printf(" Exit : #%d : low = %d ", u, low[u]); printf(" Stack: "); 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得还是倍增好写 虽然慢
嗯
#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; }
代码巨丑无比(逃