题意:给定一张有向图,问是否是仙人掌图。仙人掌图的定义是,首先,这张图是一个强连通分量,其次所有边在且仅在一个环内。

首先,tarjan可以判强连通分量是否只有一个。然后对于所有边是否仅在一个环内,我的做法是,当一个点在 tarjan 的 dfs 中,引出下一条边,如果这条边指向了一个时间轴上比它大的点,那么该点一定是 dfs 树中它的后继节点,在之前必定有一条这两点之间的路径,那么这两点之间就已经有两条路径了,而从后继节点一定能返回到祖先节点而形成环(强连通),所以返回祖先节点的路径一定与两点间的两条路径形成两个环,就不符合仙人掌图了。而如果这条边指向了一个时间轴上比它小的点,那么或者那个点是这个圈的访问祖先,或者那个点是其他圈中的点,那么这一段一定都在一个圈上,那就不断遍历回祖先将点的访问数+1。最后再判断是否每个点的访问数小于等于1就行了。

 1 #include<stdio.h>
 2 #include<string.h>
 3 #include<stack>
 4 #include<queue>
 5 using namespace std;
 6 
 7 const int maxn=2e4+5;
 8 const int maxm=5e4+5;
 9 
10 int head[maxn],point[maxm],nxt[maxm],size;
11 int n,t,scccnt;
12 int stx[maxn],low[maxn],scc[maxn];
13 int fa[maxn];
14 int vis[maxn];
15 stack<int>S;
16 bool f;
17 
18 void init(){
19     memset(head,-1,sizeof(head));
20     size=0;
21     f=1;
22     memset(vis,0,sizeof(vis));
23     memset(fa,-1,sizeof(fa));
24 }
25 
26 void add(int a,int b){
27     point[size]=b;
28     nxt[size]=head[a];
29     head[a]=size++;
30 }
31 
32 void dfs(int s,int pre){
33     fa[s]=pre;
34     stx[s]=low[s]=++t;
35     S.push(s);
36     for(int i=head[s];~i;i=nxt[i]){
37         int j=point[i];
38         if(!stx[j]){
39             dfs(j,s);
40             low[s]=min(low[s],low[j]);
41         }
42         else if(!scc[j]){
43             if(stx[j]<stx[s]){
44                 int k=s;
45                 while(k!=j&&k!=-1){
46                     vis[k]++;
47                     k=fa[k];
48                 }
49             }
50             else f=0;
51             low[s]=min(low[s],stx[j]);
52         }
53     }
54     if(low[s]==stx[s]){
55         scccnt++;
56         while(1){
57             int u=S.top();S.pop();
58             scc[u]=scccnt;
59             if(s==u)break;
60         }
61     }
62 }
63 
64 void setscc(){
65     memset(stx,0,sizeof(stx));
66     memset(scc,0,sizeof(scc));
67     t=scccnt=0;
68     for(int i=0;i<n;++i)if(!stx[i])dfs(i,-1);
69 }
70 
71 int main(){
72     int T;
73     scanf("%d",&T);
74     while(T--){
75         int m;
76         scanf("%d",&n);
77         init();
78         int a,b;
79         while(scanf("%d%d",&a,&b)&&a+b){
80             add(a,b);
81         }
82         setscc();
83         for(int i=0;i<n;++i)if(vis[i]>1)f=0;
84         if(scccnt>1||!f)printf("NO\n");
85         else printf("YES\n");
86     }
87     return 0;
88 }
View Code