P5058 【[ZJOI2004]嗅探器】(dfn数组判断割点关键点)

如果这个点是割点且删去后, a a b b 在不同的连通块就可行

现在问题关键就是如何判断这个割点是否把 a a , b b 隔开来了

那么现在我们从 a a 点开始 t a r j a n tarjan

想象一下 v v u u 的儿子,如果 d f n [ u ] < = l o w [ v ] dfn[u]<=low[v] 说明 u u 是割点

删掉 u u 后, a a v v 必定隔开来

那如果 b b v v 的一侧,不就说明 a a v v 隔开了吗?

b b v v 的一侧不就说明 d f n [ b ] > = d f n [ v ] dfn[b]>=dfn[v] 吗?

因为 v v 是继 u u 第一个被访问的,所以如果 d f n [ b ] < d f n [ v ] dfn[b]<dfn[v]

说明b在u的那一侧,删掉这个点不一定可行

#include <bits/stdc++.h>
using namespace std;
const int maxn=1e6+10;
int n,l,r;
struct edge{ int to,nxt; } d[maxn];
int head[maxn],cnt=1;
void add(int u,int v){ d[++cnt]=(edge){v,head[u]},head[u]=cnt; }
int dfn[maxn],low[maxn],stac[maxn],id,top,cut[maxn];
void tarjan(int u,int root)
{
	low[u]=dfn[u]=++id,stac[++top]=u;
	int flag=0;
	for(int i=head[u];i;i=d[i].nxt )
	{
		int v=d[i].to;
		if( !dfn[v] )
		{
			tarjan(v,root);
			low[u]=min( low[u],low[v] );
			if( dfn[u]<=low[v] )
			{
				flag++;
				if( u!=root||flag>=2 )
				{
					if( dfn[v]<=dfn[r] )	cut[u]=1;
				}
			}
		}
		else	low[u]=min( low[u],dfn[v] );
	}
}
int ans[maxn];
int main()
{
	cin >> n;
	while( cin >> l >> r && (l+r)!=0 )	add(l,r),add(r,l);
	cin >> l >> r;
	tarjan(l,l);
	for(int i=1;i<=n;i++)
	if( cut[i]&&i!=l&&i!=r )
	{
		cout << i;
		return 0;
	}
	cout << "No solution";
} 

猜你喜欢

转载自blog.csdn.net/jziwjxjd/article/details/108370943