题目链接
题意
判断一个有向图是不是仙人掌图
思路
思路看这
代码看这
据说数据水,这个比较详细应该正确
tarjan 先判断图是否强连通
再判断是否为仙人掌
假设图强连通,结论如下
仙人掌图的 DFS 树没有横向边。
设某个点 v 有 a(v)个儿子的 Low 值小于 DFS(v),同时 v 自己有 b(v)条逆向边,那么 a(v)+b(v)<2。
代码
#include <bits/stdc++.h>
using namespace std;
const int N = 200005;
vector<int> e[N];
stack<int> st;
int low[N], dfn[N], vis[N], tot, cnt;
void tarjan(int u) {
low[u] = dfn[u] = ++tot;
vis[u] = 1;
st.push(u);
for(auto v : e[u]) {
if(!dfn[v]) tarjan(v), low[u] = min(low[u], low[v]);
else if(vis[v]) low[u] = min(low[u], dfn[v]);
}
if(low[u] == dfn[u]) {
++cnt;
while(1) {
int now = st.top();
st.pop();
vis[now] = 0;
if(now == u) break;
}
}
}
int num[N];
int dfs(int u, int mindfn) {
num[u] = 1;
int backs = 0;
for(auto v : e[u]) {
if(num[v] == 1) {
++backs;
if(backs > 1 || dfn[v] < mindfn) return 0;
}
}
if(backs) mindfn = dfn[u];
for(auto v : e[u]) {
if(num[v] == 2) return 0;
if(num[v] == 0 && dfs(v,mindfn) == 0) return 0;
}
num[u] = 2;
return 1;
}
int main() {
int t;
for(scanf("%d",&t); t; --t) {
memset(low,0,sizeof(low));
memset(dfn,0,sizeof(dfn));
memset(vis,0,sizeof(vis));
tot = cnt = 0;
while(!st.empty()) st.pop();
int n;
scanf("%d",&n);
for(int i = 0; i < n; ++i) e[i].clear();
int u, v;
while(scanf("%d%d",&u,&v),u+v) e[u].push_back(v);
for(int i = 0; i < n; ++i) if(!dfn[i]) tarjan(i);
memset(num,0,sizeof(num));
printf("%s\n",cnt > 1 || dfs(0,dfn[0]) ? "YES" : "NO");
}
return 0;
}