CF999E Solution

题目链接

题解

可以发现,在一个强连通分量中,只要一个节点可以到达(s)节点,所有节点均可以到达。因此将强连通分量缩点,入度为0且不包含根节点的缩点个数即为答案。易证,入度不为0的缩点一定可以通过与它连接的缩点到达(s)节点。

AC题解

#include<bits/stdc++.h>
using namespace std;
const int N=5010;
int fst[N],nxt[N],v[N],cnt;
int c[N],dfn[N],low[N],pos,sum,s;//c[i]:编号为i的节点所在强连通分量编号
bool ins[N],in[N];//in[i]:编号为i的强连通分量是(1)否(0)有入边
stack<int> st; 
void add(int a,int b)
{
	v[++cnt]=b;
	nxt[cnt]=fst[a]; fst[a]=cnt;
}
void tarjan(int x)
{
	dfn[x]=low[x]=++pos;
	st.push(x); ins[x]=1;
	for(int i=fst[x];i;i=nxt[i])
	{
		int y=v[i];
		if(!dfn[y]) {tarjan(y); low[x]=min(low[x],low[y]);}
		else if(ins[y]) low[x]=min(low[x],dfn[y]);
	}
	if(dfn[x]==low[x])
	{
		c[x]=++sum; ins[x]=0;
		while(st.top()!=x) 
		{
			int t=st.top(); st.pop();
			c[t]=sum; ins[t]=0;
		}
		st.pop();
	}
}
int main()
{
	int n,m,ans=0;
	scanf("%d%d%d",&n,&m,&s);
	int x,y;
	for(int i=1;i<=m;i++) {scanf("%d%d",&x,&y); add(x,y);}
    //tarjan缩点
	for(int i=1;i<=n;i++)
		if(!dfn[i]) tarjan(i);
    //统计入度
	for(int i=1;i<=n;i++)
		for(int j=fst[i];j;j=nxt[j])
			if(c[v[j]]!=c[i]) in[c[v[j]]]=1;
	in[c[s]]=1;//不可以计算包含根节点的强连通分量
    //统计答案
	for(int i=1;i<=sum;i++) ans+=(in[i]^1);
	printf("%d",ans);
	return 0;
}
原文地址:https://www.cnblogs.com/violetholmes/p/14227283.html