题意让判断一个图是不是仙人掌图。
仙人掌图就是一个强连通图并且一条边只属于一个环。
判断强连通用一次tarjan即可,然后tarjan的dfs过程中可以判环。
记录每个未访问节点的父亲节点,如果一个节点被访问过,就沿着他的父亲节点找回去,给路径上的每个点的度数都加一,如果一个点的度数大于1,说明这不是仙人掌图。
#include<cstdio>
#include<cstring>
#include<stack>
#include<algorithm>
using namespace std;
const int maxn = 20005;
int n;
struct node
{
int to;
int next;
node(){}
node(int a,int b):to(a),next(b){}
}edge[50005];
int head[maxn],dfn[maxn],low[maxn],vis[maxn],fa[maxn],in[maxn];
int tot,ti,cnt,flag;
void clo(int u,int v)
{
while(fa[u]!=v)
{
in[u]++;
if(in[u]>1) {flag = 1;return ;}
u = fa[u];
}
}
void tarjan(int u)
{
if(flag==1) return;
dfn[u] = ++ti;
low[u] = ti;
vis[u] = 1;
for(int i=head[u];i!=-1;i=edge[i].next)
{
if(flag==1) return;
int v = edge[i].to;
if(!vis[v])
{
fa[v] = u;
tarjan(v);
low[u] = min(low[u],low[v]);
}
else if(vis[v]==1)
{
low[u] = min(low[v],dfn[v]);
clo(u,v);
}
}
if(dfn[u]==low[u])
{
cnt++;
if(cnt>1) flag = 1;
}
}
int main()
{
int cases,u,v;
scanf("%d",&cases);
while(cases--)
{
scanf("%d",&n);
memset(head,-1,sizeof(head));
tot = 0;
while(scanf("%d%d",&u,&v))
{
if(u==0&&v==0) break;
edge[tot] = node(v,head[u]);
head[u] = tot++;
}
flag = 0;
ti = 0;
cnt = 0;
memset(dfn,0,sizeof(dfn));
memset(vis,0,sizeof(vis));
memset(low,0,sizeof(low));
memset(in,0,sizeof(in));
memset(fa,0,sizeof(fa));
for(int i=0;i<n;i++) if(!vis[i]) tarjan(i);
if(flag) printf("NO\n");
else printf("YES\n");
}
return 0;
}