因为a,b间所有的信息都要经过中间服务器,说明中间服务器删掉后,ab不连通,这个中间服务器一定是一个割点。
我们在用Tarjan求割点时,要判断割点是否把 a 和 b 必须分割到两个不连通的部分中。
为了简化问题,我们直接把a点作为根,从a点开始做DFS。
最容易想到的就是判断b的时间戳(dfn)是否不小于我们找到的割点,如下图
但是这种想法是有缺陷的。
假如a有一条连向b的边,但b却是由u遍历到的,那么这时u就不能分开a与b。
所以我们想到利用判断 u是割点的 v点
对于一个割点u,若判定u为割点的边是(u,v):
1.dfn[b]<dfn[u],此时b是u的祖先或者b和u不在同一个子树内。由于a是根,那么a和b一定在一个连通块内。
2.dfn[b]>dfn[u]&&dfn[b]<dfn[v],此时b是v的兄弟子树中的节点,无法确定 a,b是否在一个连通块。
3.dfn[b]>=dfn[v],此时b是v的子树中的节点,a,b一定不在一个连通块。
想清楚这些,AC就向你挥手了。
代码如下:
#include<bits/stdc++.h>
using namespace std;
const int N=1000005;//看起来舒服
int n,m,root,low[N],dfn[N],x,y,ans[N],tot,point[N],aa,bb,ff;
vector<int> g[N];//邻接表
//不需要栈了,因为不是缩点,不需要给所属强连通分量编号
void tanjan(int u){
low[u]=dfn[u]=++tot;
for(int i=0;i<g[u].size();i++)
{
if(!dfn[g[u][i]])//没到过
{
tanjan(g[u][i]);
if(low[g[u][i]]>=dfn[u])
{
if(root!=u)//非根节点
{
if(dfn[bb]>=dfn[g[u][i]])
{
point[u]=1;//是所求的点。注意这个点是割点,但不是所有割点。
ff=1;//用于输出判断
}
}
}
else
{
low[u]=min(low[u],low[g[u][i]]);
}
}
else
{
low[u]=min(low[u],dfn[g[u][i]]);
}
}
}
int main(){
cin>>n;
x=1;
y=1;
while(x&&y)
{
cin>>x>>y;
g[x].push_back(y);
g[y].push_back(x);
}
cin>>aa>>bb;
root=aa;
tanjan(aa);//tanjan a就行,简化问题
int j=0;
for(int i=1;i<=n;i++)
{
if(point[i]!=0)
{
ans[j]=i;
j++;
}
}
if(ff==0)
cout<<"No solution";
else
{
cout<<ans[0];
}//输出判断。
return 0;
}
祝大家儿童节快乐!