版权声明:转载注明下出处就行了。 https://blog.csdn.net/LJD201724114126/article/details/84474928
强联通分量参考神犇博客:
https://www.byvoid.com/zhs/blog/scc-tarjan
https://www.luogu.org/blog/styx-ferryman/chu-tan-tarjan-suan-fa-qiu-qiang-lian-tong-fen-liang-post
题目链接:传送门
题解:直接弄个强联通分量,然后枚举每条边,假设x ,y不在同一个强联通块,那么就让强联通块x出度加1,最后找到出度为0的强联通块就行了,但只能有一个,要是超过了一个,就不符合题意了。
代码如下:
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
const int maxn=50010;
int x[maxn],y[maxn];
int DFN[maxn],LOW[maxn],next[maxn],point[maxn],v[maxn],vis[maxn];
int belong[maxn],top,tot,cnt,sizes[maxn],mark[maxn],stacks[maxn],index;
void add(int x,int y){
tot++;
next[tot]=point[x];
point[x]=tot;
v[tot]=y;
}
void tarjan(int x) ///强联通模板
{
stacks[++top]=x; ///此点进栈
vis[x]=1; ///标记是否已经访问过
DFN[x]=LOW[x]=++index; ///给编号
for(int i=point[x];i;i=next[i])
{
int j=v[i];
if(!DFN[j]){
tarjan(j);
LOW[x]=min(LOW[x],LOW[j]);
}
else if(vis[j]) LOW[x]=min(LOW[x],LOW[j]);
}
if(LOW[x]==DFN[x])
{
cnt++; ///有向图强联通分量有多少块
int item;
do{
item=stacks[top--];
vis[item]=0;
belong[item]=cnt; ///表示点item属于cnt强联通块
sizes[cnt]++; ///表示cnt块有多少个点
}while(item!=x);
}
}
int main()
{
int n,m;
while(~scanf("%d%d",&n,&m))
{
for(int i=1;i<=m;i++){
scanf("%d%d",&x[i],&y[i]);
add(x[i],y[i]);
}
memset(DFN,0,sizeof(DFN)); ///初始化
memset(LOW,0,sizeof(LOW));
memset(vis,0,sizeof(vis));
memset(vis,0,sizeof(vis));
top=tot=index=0;
for(int i=1;i<=n;i++)
{
if(!DFN[i]) tarjan(i);
}
memset(mark,0,sizeof(mark));
for(int i=1;i<=m;i++){
if(belong[x[i]]!=belong[y[i]]){ ///表示有两块
mark[belong[x[i]]]++; ///在第一块初度加1
}
}
int total=0,flag=true;
for(int i=1;i<=cnt;i++)
{
if(!mark[i]){
if(total){ ///表示至少有两块出度为0的块,输出0
flag=false;break;
}
total=i;
}
}
// printf("total=%d\n",total);
if(flag) printf("%d\n",sizes[total]); ///只要一块出度为0的块
else printf("0\n");
}
return 0;
}
我的标签:做个有情怀的程序员。