Codeforces Round #490 (Div. 3) E.Reachability from the Capital (强连通分量/Tarjan+dfs)

题目

n个城市,m条单向边(n,m<=5e3)

问至少需要加多少条单向边,

才能使得从源点s出发,所有点都可达

思路来源

https://www.cnblogs.com/shinianhuanniyijuhaojiubujian/p/9242441.html

题解

大概就是把图搞成全连通分量

原来求完强连通分量之后,是可以重建图然后dfs新图的……

这个东西用vector存更方便,毕竟能用两个不同的vector区分原图和现在的图

而链式前向星要么得head2[]、cnt2,要么求完tarjan之后把图初始化了

先求强连通分量,然后对s所在的强连通分量进行dfs

s可达的强连通分量显然对答案没有贡献,

其余的入度为0的强连通分量即为所求

代码

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int maxn=1e4+5;
const int maxm=5e4+5;
int n,m,s;
int u[maxm],v[maxm];
int ans;//最后答案 出度为0点的个数 
int head[maxn],cnt;
int low[maxn],dfn[maxn],num;//最早非负祖先时间戳 时间戳 
int stack[maxn],top,now;//用数组模拟栈 栈顶 当前栈顶值 
int par[maxn],tot;//染色 颜色数 
bool in[maxn];//是否在栈中 
bool vis[maxn];//该连通分量是否被访问过 
int IN[maxn];//如果不在一个连通分量里 统计出度  
int sum[maxn];//每个连通分量里的点的个数 
struct edge
{
	int to,next;
}e[maxm];
void add(int u,int v)
{
	e[++cnt].to=v;
	e[cnt].next=head[u];
	head[u]=cnt;
}
void dfs(int u)
{
	low[u]=dfn[u]=++num;
	in[u]=1;
	stack[++top]=u;
	for(int i=head[u];i;i=e[i].next)
	{
		int v=e[i].to;
		if(!dfn[v])
		{
			dfs(v);
			low[u]=min(low[u],low[v]);
		}
		else if(in[v])
		{
			low[u]=min(low[u],dfn[v]);
		}
	}
	if(low[u]==dfn[u])//环的第一个点 
	{
		tot++;
		do
		{
			now=stack[top--];
			par[now]=tot;
			sum[tot]++; 
			in[now]=0; 
		}while(now!=u);
	}
}
void init1()
{
	memset(head,0,sizeof head);
	cnt=0;
}
void init2()
{
	memset(low,0,sizeof low);
	memset(dfn,0,sizeof dfn);
	memset(par,0,sizeof par);
	memset(sum,0,sizeof sum);
	memset(in,0,sizeof in);
	memset(IN,0,sizeof IN); 
	ans=num=top=tot=0;
}
void dfs2(int u)
{
	vis[u]=1;
	for(int i=head[u];i;i=e[i].next)
	{
		int v=e[i].to;
		if(vis[v])continue;
		dfs2(v); 
	}
} 
int main()
{
	while(~scanf("%d%d%d",&n,&m,&s))
	{
		init1();//重新建图 
		init2();
		for(int i=1;i<=m;++i)
		{
			scanf("%d%d",&u[i],&v[i]);
			add(u[i],v[i]);
		}
		for(int i=1;i<=n;++i)
	    if(!dfn[i])dfs(i);
	    init1();//重新建图 
		for(int i=1;i<=m;++i)
	    {
	     if(par[u[i]]==par[v[i]])continue;
	     add(par[u[i]],par[v[i]]);
	     IN[par[v[i]]]++;//u->v 
	    }
	    dfs2(par[s]);
	    for(int i=1;i<=tot;++i)
	    if(!IN[i]&&!vis[i])ans++;//从par[s]出发不可达 且入度为0的分量 
		printf("%d\n",ans);
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/Code92007/article/details/89067363