(強連通分量)洛谷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;
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章