不知道noip考不考这个,但是既然复习到了为了退役之后不忘还是写一下吧。关于这个强连通分量我只知道一个tarjan算法。
基本过程:dfs一个图把搜到的点进栈,对每个点进行标两个序号,一个表示深搜的顺序,另一个表示这个点所能直接或间接连到的被搜索到的最早的点,要一直维护第二个值,在回溯的时候(子树处理完后)如果这个点的两序号相等,则这个点就是这个强连通分量最早被搜到的点,那么这个时候进行出栈操作,从栈顶到这个点都是当前搜到的强连通分量里的点(一个点也可以是强连通分量,会出栈,使得之后找到的强连通分量里不包含这个点,当然如果这个点属于别的强连通分量里就不会单独出栈了)。
不是很纯粹的模板题codevs 2822 爱在心中
除了找所有的强连通分量之外还要对这些强连通分量进行判断,如果这个强连通分量里所有的点的所有边都在强连通分量内部,那么这个就是所求的强连通分量,如果出现了多个那么所求的强连通分量就不存在。因为如果这个强连通分量有向外的边,那么这个强连通分量就不可能是最终答案,可以联系一下并查集找最终的中枢。
#include<cstdio>
#include<iostream>
#include<vector>
using namespace std;
vector<int> bian[100010];
int dfn[50010],low[50010],cnt=0; //dfn是dfs的顺序,low是能走到的顺序最早的点,cnt用于标序号
int belong[50010],num=0,ans=0; //属于哪一个强连通分量,有多少个强连通分量,目标强连通分量
int zhan[50010],tot=0; //栈,栈顶
bool inzhan[50010]={0};
int n,m;
bool pd=0;
int min(int a,int b)
{
return (a<b)?a:b;
}
void tarjan(int d)
{
dfn[d]=low[d]=++cnt;
zhan[++tot]=d;
inzhan[d]=1;
int len=bian[d].size(),a;
for (int i=0;i<len;i++)
{
a=bian[d][i];
if (!dfn[a])
{
tarjan(a);
low[d]=min(low[d],low[a]);
}
else if (inzhan[a]) low[d]=min(low[d],dfn[a]);
}
if (dfn[d]==low[d])
{
num++;
a=0;
int la=0;
while (a!=d)
{
a=zhan[tot--];
la++;
belong[a]=num;
inzhan[a]=0;
}
if (la==1) //删去大小为一的强连通分量,题目要求不能自恋。。。
{
belong[d]=0;
num--;
return ;
}
for (int i=tot+1;i<=tot+la;i++)
{
len=bian[zhan[i]].size();
for (int j=0;j<len;j++)
if (belong[bian[zhan[i]][j]]!=num)
return ;
}
if (ans) {pd=1;return ;}
ans=num;
}
}
int main()
{
cin>>n>>m;
int a,b;
for (int i=1;i<=m;i++)
{
scanf("%d%d",&a,&b);
bian[a].push_back(b);
}
for (int i=1;i<=n;i++)
if (!dfn[i])
tarjan(i);
cout<<num<<endl;
if (pd||!ans) cout<<"-1";
else
{
for (int i=1;i<=n;i++)
if (belong[i]==ans)
printf("%d ",i);
}
return 0;
}