【luogu P2002】消息扩散

时间:2023-03-08 23:33:58
【luogu P2002】消息扩散

https://www.luogu.org/problem/show?pid=2002

SCC缩点的模板题,缩点后统计入度为0的点的数量就完了。

#include <iostream>
#include <vector>
#include <stack>
#include <cstring>
#define maxn 100005
using namespace std;
int n, m;
vector<int> g[maxn], g2[maxn];
int indegree[maxn]; int timer = , cnter = ;
int dfn[maxn], low[maxn], scc[maxn];
stack<int> sta;
bool insta[maxn];
void dfs(int v)
{
/*
low[v]=min{
dfn[v],
low[w], 存在有向边(v,w)且搜索到v时w尚未被搜索到
dfn[w] 存在有向边(v,w)且搜索到v时w在栈内
}
*/
low[v] = dfn[v] = ++timer;
sta.push(v);
insta[v] = true;
for (int i = ; i < g[v].size(); i++)
{
int w = g[v][i];
if (!dfn[w])
{
dfs(w);
low[v] = min(low[v], low[w]);
}
else if (insta[w])
{
low[v] = min(low[v], dfn[w]);
}
}
if (dfn[v] == low[v])
{
++cnter;
int t;
do
{
t = sta.top();
sta.pop();
insta[t] = false;
scc[t] = cnter;
} while (t != v);
}
}
void tarjan_scc()
{
for (int i = ; i <= n; i++)
{
if (!dfn[i])
dfs(i);
}
}
void contract()
{
for (int v = ; v <= n; v++)
{
for (int i = ; i < g[v].size(); i++)
{
// 对于边(v,w),若v与w不在同一SCC,则该边在缩点后的新图为(scc[v],scc[w])
int w = g[v][i];
if (scc[v] != scc[w])
{
g2[scc[v]].push_back(scc[w]);
indegree[scc[w]]++;
}
}
}
}
int main()
{
ios::sync_with_stdio(false);
cin >> n >> m;
int u, v;
for (int i = ; i <= m; i++)
{
cin >> u >> v;
g[u].push_back(v);
}
tarjan_scc();
contract(); // 统计得到的DAG中入度为0的点
int ans = ;
for (int i = ; i <= cnter; i++)
{
if (!indegree[i])
ans++;
}
cout << ans << endl;
return ;
}