思路:把每个强连通分量缩成点,缩出来的那个点的权值为整个强连通分量的点数,再求出新图里每个点能被多少个其它点(该点所代表的强连通分量点数)到达,最后看每个点所在缩点能否被图中所有点到达。
看测试点是错误在答案少了。
#include <iostream>
#include <vector>
#include <stack>
#include <queue>
using namespace std;
const int N = 10005, M = 50005;
int n, m, Sd[N], In[N], sum[N];
int cnt, dfn[N], Low[N], vis[N];
int tot, head[N], to[M << 1], nxt[M << 1], frm[M << 1];
vector<int> edg[N];
stack<int> stk;
queue<int> que;
void add(int x, int y){
to[++tot] = y;
frm[tot] = x;
nxt[tot] = head[x];
head[x] = tot;
}
void Tarjan(int x){
Low[x] = dfn[x] = ++cnt;
stk.push(x), vis[x] = 1;
for(int i = head[x]; i; i = nxt[i]){
if(!dfn[to[i]])
Tarjan(to[i]), Low[x] = min(Low[x], Low[to[i]]);
else if(vis[to[i]])
Low[x] = min(Low[x], dfn[to[i]]);
}
if(Low[x] == dfn[x]){
while(stk.top() != x){
Sd[stk.top()] = x, sum[x] += sum[stk.top()];//缩出来的那个点的权值求和为整个强连通分量的点数
vis[stk.top()] = 0, stk.pop();
}
Sd[x] = x, vis[x] = 0, stk.pop();
}
}
int Topo(){
int x, res = 0;
for(int i = 1; i <= n; i++)
if(Sd[i] == i && !In[i]) que.push(i);//把入度为0的点入队
while(!que.empty()){
x = que.front(), que.pop();
for(int i : edg[x]){
sum[i] += sum[x], In[i]--;//求出该强连通分量能被多少个点到达
if(!In[i]) que.push(i);
}
}
for(int i = 1; i <= n; i++)
if(sum[Sd[i]] == n) res++;//答案为能被所有点到达的点数
return res;
}
int main(){
int x, y;
scanf("%d%d", &n, &m);
for(int i = 1; i <= n; i++)
sum[i]++;//一开始每个点的点数为1
for(int i = 1; i <= m; i++){
scanf("%d%d", &x, &y);
add(x, y);
}
for(int i = 1; i <= n; i++)
if(!dfn[i]) Tarjan(i);
for(int i = 1; i <= m; i++){
x = Sd[frm[i]], y = Sd[to[i]];
if(x != y) In[y]++, edg[x].push_back(y);
}
printf("%d\n", Topo());
return 0;
}