~~~~~ P2863 [USACO06JAN] The Cow Prom S ~~~~~ 总题单链接
强连通分量的定义
~~~~~ 强连通分量:原图一个极大子图使得从这个子图中的任意一个点出发都可以到达这个子图的任意一个点。
怎么找到每个点对应的强连通分量
~~~~~ 按 d f s dfs dfs序 访问图上的每个点,每个点只访问一遍,每个点第一次访问的时候加入到栈里。
~~~~~ 先来看看时间戳的定义,若一个点的时间戳为 x x x,那它就是在 d f s dfs dfs 时第 x x x 个被访问到的点。
~~~~~ 对于图上的每个点,记录 d f n [ i ] dfn[i] dfn[i] 表示 点 i i i 的时间戳, l o w [ i ] low[i] low[i] 表示 点 i i i 在不走进入点 i i i 的点的情况下能走到的最早时间戳。举个栗子,在 d f s dfs dfs 的时候是从 u u u 走到 v v v 的,那 l o w [ v ] low[v] low[v] 就是 v v v 在不仅过过 u u u 的情况下能走到的最早时间戳。
~~~~~ 若在离开点 u u u 时, l o w [ v ] = d f n [ u ] low[v]=dfn[u] low[v]=dfn[u] 则当前还在栈内的点就是一个强连通分量。为什么?因为 l o w [ v ] = d f n [ u ] low[v]=dfn[u] low[v]=dfn[u] 说明从当前栈内点出发在不经过 u u u 的情况下到不了比 u u u 时间戳更小的点,所以这些点是一个强连通分量。对这句话理解有问题的同学可以再看一看强连通分量的定义。
模板题代码
~~~~~ 缩点值后就用 d p dp dp 来统计答案。
#include<bits/stdc++.h>
#define ll long long
using namespace std;
vector<ll>eg[10005];
ll dfn[10005],low[10005],tot;
ll pos[10005],siz[10005],cnt;
ll n,m,stk[10005],init[10005],top;
void Tarjan(ll p){
dfn[p]=low[p]=++tot;
stk[++top]=p;init[p]=1;
for(ll v:eg[p]){
if(!dfn[v]){
Tarjan(v);
low[p]=min(low[p],low[v]);
}
else if(init[v])low[p]=min(low[p],low[v]);
}
if(dfn[p]==low[p]){
cnt++;
while(tot>=1){
ll v=stk[top--];
init[v]=0;
pos[v]=cnt;
siz[cnt]++;
if(v==p)break;
}
}
}
signed main(){
ios::sync_with_stdio(false);
cin>>n>>m;
while(m--){
ll x,y;cin>>x>>y;
eg[x].push_back(y);
}
for(ll i=1;i<=n;i++)
if(!dfn[i])Tarjan(i);
ll ans=0;
for(ll i=1;i<=cnt;i++)
if(siz[i]>1)ans++;
cout<<ans;
return 0;
}