题解 P5058 【[ZJOI2004]嗅探器】

因为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;
}

祝大家儿童节快乐!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值