题解 CF999E 【Reachability from the Capital】

(quad)前置芝士:Tarjan缩点,建议做做模板题(P3387 【模板】缩点)

[ ext{关于题意} ]

(quad)给定一个有向图,求要使一个点 (t) 能够到达其他所有点还需加多少边?

(quad)因为在一个强连通分量中,每个点都可以到达其他所有点,所以考虑缩点,将一个强连通分量中所有点看做一个点,再做其他操作。

(quad)缩点之后图就变成了(DAG)(有向无环图),然后考虑每一个点的入度情况(缩点后),因为点 (t) 要能到达其他每一个点,所以除了点 (t) 所在的强连通分量之外,其他所有点的入度必须大于(0),通过找规律就可以发现答案就是缩点后入度为 (0) 且不是点 (t) 所在的强连通分量的点的数量。(因为即使t所在的强连通分量入度为0,也不需要连边)

(quad)如果还不能理解就看代码吧,注释都在代码里。

#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<queue>
#include<stack>
#include<algorithm>
#define int long long
#define re register int
#define il inline
#define next ne
using namespace std;
il int read()					     //快速读入
{
	int x=0,f=1;char ch=getchar();
	while(isdigit(ch)==0&&ch!='-')ch=getchar();
	if(ch=='-')f=-1,ch=getchar();
	while(isdigit(ch))x=(x<<1)+(x<<3)+(ch-'0'),ch=getchar();
	return x*f;
}
const int N=5e3+5;
int n,m,dfn[N],low[N],c[N],f[N*2],s[N],l[N]
int next[N*2],go[N*2],head[N],tot,cnt,ans,t;
il void Add(int x,int y)  			     //链式前向星存图,记录每条边的端点u,v,之后要计算每个点的入度
{
	next[++tot]=head[x];
	head[x]=tot;
	f[tot]=x;
	go[tot]=y;
}
stack<int>q;					     //个人喜欢用stack栈,不用数组模拟栈
il void Tarjan(int x)				     //Tarjan缩点板子
{
	dfn[x]=low[x]=++cnt;
	q.push(x);
	for(re i=head[x];i;i=next[i])
	{
		int y=go[i];
		if(!dfn[y])Tarjan(y),low[x]=min(low[x],low[y]);
		else if(!c[y])low[x]=min(low[x],dfn[y]);
	}
	if(dfn[x]==low[x])
	{
		c[x]=++c[0];
		s[c[0]]=1;
		while(q.top()!=x)
		{
			c[q.top()]=c[0];
			s[c[0]]++;
			q.pop();
		}
		q.pop();
	}
}
signed main()
{
	n=read();m=read();t=read();
	tot=cnt=0;
	for(re i=1;i<=m;i++)
	{
		int x=read(),y=read();
		Add(x,y);
	}
	for(re i=1;i<=n;i++)
	if(!dfn[i])Tarjan(i);
	if(c[0]==1){printf("0
");return 0;}	//如果只有一个强连通分量,不需要加边
	for(re i=1;i<=m;i++)
	{
		int x=c[f[i]],y=c[go[i]];
		if(x!=y)l[y]++; 		//记录每个点的入度(缩点后)
	}
	for(re i=1;i<=c[0];i++)			//条件1:入度为0   条件2:不是t所在的强连通分量
	if(!l[i]&&i!=c[t])ans++;		//统计答案
	printf("%lld
",ans);
	return 0;
}

(quad)如果觉得我写得好的,不妨点个赞呗

原文地址:https://www.cnblogs.com/FarkasW/p/13916608.html