(强连通分量)洛谷P2341【模板】强连通分量 / [HAOI2006]受欢迎的牛

洛谷P2341【模板】强连通分量 / [HAOI2006]受欢迎的牛

思路:

如题目描述,模板题。
花了两个小时看了tarjan,然后看了题目。what?excuse me?为啥跟我看到的东西感觉不太一样?然后看了大佬的题解,what?为啥叫容易看出,为啥我看不出?
啥叫强连通分量呢,简而言之,一个图的子图中任意两点可以相互到达。(就是构成了一个环)。
下面说tarjan算法。
其中两个重要的数组:
dfn:搜索的次序;low:这个点及其子孙节点中dfn的最小值。就是确定这个点所属的强连通分量。low相等就是这几个点所处于同一个强连通分量。
我们用栈stack记录搜素的路径,vis记录是否已经访问过。
在一个结点出边遍历完了之后,我们回溯判断low。如果dfn[x]==low[x]则可以看作这是某一强连通分量的根节点。然后不断出栈,直到x被弹出。

tarjan核心代码:

void tarjan(int u)
{
	int i;
	cnt++;
	dfn[u]=low[u]=cnt;
	s.push(u);
	vis[u]=1;
	for(i=0;i<edge[u].size();i++)
	{
		int v=edge[u][i];
		if(!dfn[v])
		{
			tarjan(v);
			low[u]=min(low[u],low[v]);
		}
		else if(vis[v])
			low[u]=min(low[u],dfn[v]);
		
	}
	if(dfn[u]==low[u])
	{
		tot++;
		int pre;
		do
		{
			pre=s.top();
			s.pop();
			vis[pre]=0;
			ans[tot].push_back(pre);
		}while(pre!=u);
	}
}

对于这题,我们容易看出……
我们可以看出如果牛互相爱慕能够构成一个强连通分量(环),那么之外的任意一头牛爱慕其中的一个牛,就能爱慕这个分量中其他的牛;同理,这个分量中某头牛爱慕其他的牛,那么这个分量中的其他的牛也会爱慕那个牛。
然后我们可以缩点,把图变成有向无环图。
接着可以发现,如果一个强连通分量的出度为0,那么这个强连通分量中牛的个数就是答案。因为这是有向无环图,所以如果一个强连通分量能到另外一个分量,那么他一定不会被另外的分量到达(听起来好绕)。就比如两个分量a,b,如果存在a->b,那么不可能存在b->a,因为我们已经通过缩点把环都给缩成了一个点。
所以我们只要找出度为0的强连通分量就行了,但是如果有多个强连通分量的出度都为0,那么就没有牛成为明星,因为不是所有的牛都爱慕他(们)。这点很好理解。
我们把在同一个强连通分量的点用color标好。用out记录出度。

代码:

#include<bits/stdc++.h>
#define pii pair<int,int>
#define ll long long
#define cl(x) memset(x,0,sizeof(x))
const int N=1e6+10;
const int mod=1e7+9;
const int maxn=0x3f3f3f3f;
const int minn=0xc0c0c0c0;
const int inf=99999999;
using namespace std;
struct edge
{
	int next,to;
}a[N];
int head[11000]={0}, dfn[11000]={0},low[11000],vis[11000]={0},out[11000],color[11000],sum[11000]={0};
int len=0,tot=0,cnt=0;
stack<int> s;
int add(int u,int v)
{
	a[++len]={head[u],v};
	head[u]=len;
}
void tarjan(int u)
{
	int i;
	cnt++;
	dfn[u]=low[u]=cnt;
	s.push(u);
	vis[u]=1;
	for(i=head[u];i;i=a[i].next)
	{
		int v=a[i].to;
		if(!dfn[v])
		{
			tarjan(v);
			low[u]=min(low[u],low[v]);
		}
		else if(vis[v])
			low[u]=min(low[u],dfn[v]);
	}
	if(dfn[u]==low[u])
	{
		tot++;
		int pre;
		do
		{
			pre=s.top();
			s.pop();
			vis[pre]=0;
			color[pre]=tot;
			sum[tot]++;
		}while(pre!=u);
	}
}
int main()
{
	ios::sync_with_stdio(false);
	cin.tie(0);cout.tie(0);
	int n,m,i,j;
	cin>>n>>m;
	for(i=1;i<=m;i++)
	{
		int u,v;
		cin>>u>>v;
		add(u,v);
	}
	for(i=1;i<=n;i++)
		if(!dfn[i])
			tarjan(i);	
	for(i=1;i<=n;i++)
		for(j=head[i];j;j=a[j].next)
			if(color[i]!=color[a[j].to])
				out[color[i]]++;
	int ans=0;
	for(i=1;i<=tot;i++)
	{
		if(out[i]==0)
		{
			if(ans)
			{
				cout<<"0"<<endl;
				return 0;
			}
			ans=i;
		}
	}
	cout<<sum[ans]<<endl;
	return 0;
}

发布了90 篇原创文章 · 获赞 0 · 访问量 1854

猜你喜欢

转载自blog.csdn.net/Z7784562/article/details/104081030