难度的话在于不好转换成图来思考。
其实这里的有序对,a认为b,b认为c,并且具有传递性我们就可以考虑用一条链把abc穿起来,从a能到c表示a对c崇拜,就类似与拓扑结构,入度为0的点没有人崇拜,随着链走,任意节点都是被以前的所有节点崇拜,那么最后一个节点就被所有的崇拜。
这是一条的链的情况。
这道题是多条,也可以这么思考。
假设,a被所有牛崇拜,a崇拜b,那么b肯定也被其他所有牛崇拜,因为b也崇拜a,这样就做成了一个环,我们将强连通图分量抽象成点,那么,这就转换成一条链了,问题是,最后一条链有多少个点在里面。
注意这道题坑点在于强连通图最后一个可能不是答案,因为可能这条链并不是联通的,所以拓扑排序过后,还要继续反向遍历一遍,看是不是这个强连通分量能够到达所有的点。(因为强连通图第二次dfs时是反向图,所以我们得到的拓扑排序最大的点,其实是叶子,正向进行的最后一个点。即dag的最后一个点,所以需要rdfs)
#include<iostream>
#include<string>
#include<algorithm>
#include<cmath>
#include<cstdlib>
#include<climits>
#include<stack>
#include<vector>
#include<queue>
#include<set>
#include<map>
//#include<regex>
#include<cstdio>
#define up(i,a,b) for(int i=a;i<b;i++)
#define dw(i,a,b) for(int i=a;i>b;i--)
#define upd(i,a,b) for(int i=a;i<=b;i++)
#define dwd(i,a,b) for(int i=a;i>=b;i--)
//#define local
typedef long long ll;
const double esp = 1e-6;
const double pi = acos(-1.0);
const int INF = 0x3f3f3f3f;
const int inf = 1e9;
using namespace std;
typedef pair<int, int> pir;
int n, m;
vector<int >g[10005];
vector<int >r[10005];
vector<int >vs;
bool used[10005];
int cmp[10005];
void addedge(int f, int s)
{
g[f].push_back(s);
r[s].push_back(f);
}
void dfs(int v)
{
used[v] = true;
up(i, 0, g[v].size())
{
if (!used[g[v][i]])dfs(g[v][i]);
}
vs.push_back(v);
}//正向后续遍历,叶子节点先到,即越是链的末尾,越先遍历到,这样才能做强连通图的判定
void rdfs(int v, int k)
{
used[v] = true;
cmp[v] = k;
up(i, 0, r[v].size())
{
if (!used[r[v][i]])rdfs(r[v][i], k);
}
}
int scc()//强连通分量的计算
{
memset(used, 0, sizeof(used));
vs.clear();
upd(v, 1, n)
if (!used[v])dfs(v);//先正向遍历
memset(used, 0, sizeof(used));
int k = 0;
dwd(i, vs.size() - 1, 0)
if (!used[vs[i]])rdfs(vs[i], k++);//反向遍历并且拓扑
return k-1;//减一,返回拓扑排序最大块。
}
int main()
{
cin >> n >> m;
int x, y;
up(i, 0, m)
{
scanf("%d%d", &x, &y);
addedge(x, y);
}
int temp = scc();
int ans = 0;
int u;
upd(i, 1, n)
if (cmp[i] == temp)//查看属于这个块的人数
{
u = i;
ans++;
}
memset(used, 0, sizeof(used));//重新从顶点往回遍历
rdfs(u, 0);
upd(i, 1, n)
if (!used[i])//如果有没有被访问到的,说明有独立块,肯定就是互相看不上了
ans = 0;
cout << ans << endl;
return 0;
}