洛谷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;
}