题意:给定一张有向图,问是否是仙人掌图。仙人掌图的定义是,首先,这张图是一个强连通分量,其次所有边在且仅在一个环内。
首先,tarjan可以判强连通分量是否只有一个。然后对于所有边是否仅在一个环内,我的做法是,当一个点在 tarjan 的 dfs 中,引出下一条边,如果这条边指向了一个时间轴上比它大的点,那么该点一定是 dfs 树中它的后继节点,在之前必定有一条这两点之间的路径,那么这两点之间就已经有两条路径了,而从后继节点一定能返回到祖先节点而形成环(强连通),所以返回祖先节点的路径一定与两点间的两条路径形成两个环,就不符合仙人掌图了。而如果这条边指向了一个时间轴上比它小的点,那么或者那个点是这个圈的访问祖先,或者那个点是其他圈中的点,那么这一段一定都在一个圈上,那就不断遍历回祖先将点的访问数+1。最后再判断是否每个点的访问数小于等于1就行了。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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 }