题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=3594
性质:
先给出一个仙人掌的详解: https://files-cdn.cnblogs.com/files/ambition/cactus_solution.pdf
我稍微说一下自己目前大概的理解吧,仙人掌树刚刚入门做完板题的说,网上也没什么人解释,只能自己先大概讲讲,如果有问题的欢迎指正。
仙人掌图按照图的边的方向性分两种,有向仙人掌图和无向图仙人掌图。
有向图仙人掌图
有向图仙人掌图,按照目前能看得到的资料,首先要满足的是一个整个的强连通图,每一条边必须在且仅在一个强连通块内。
所以就有了它的三条性质;
① 仙人掌图的
d
f
s
dfs
dfs 树不存在横向边。(横向边的定义可以看上面链接中的图,大概意思就是dfs的过程中不存在访问到的新点是曾经被访问过的点)
②
l
o
w
[
v
]
<
=
d
f
n
[
u
]
low[v] <= dfn[u]
low[v]<=dfn[u] (
f
a
[
v
]
=
u
fa[v]=u
fa[v]=u )
③ 设某个点
u
u
u 有可到达的点集为
S
(
u
)
S(u)
S(u) 那么满足条件
v
∈
S
(
u
)
v\in S(u)
v∈S(u) 且
l
o
w
[
v
]
<
d
f
n
[
u
]
low[v]<dfn[u]
low[v]<dfn[u] 的点
v
v
v 的个数小于
2
2
2 (
v
v
v 也可能为
u
u
u 的父链结点)
无向仙人掌图
已有知识来看,无向仙人掌图中要求所有的边,是桥或者只在一个简单环中。
bzoj上好像有一些相应的题目。。但是现在oj炸了。。我得先去学了。。
hdu3594代码
代码
#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define rep_e(i,u) for(int i=head[u];~i;i=nex[i])
using namespace std;
const int maxn=20005;
const int maxm=50005;
typedef long long ll;
int dfn[maxn],low[maxn],sta[maxn],top,flag,n,belong[maxn];
int head[maxn],to[maxm],nex[maxm],cnt,vis[maxn],qiang;
void add(int u,int v){
to[cnt]=v;nex[cnt]=head[u];
head[u]=cnt++;
}
void tarjan(int x){
if(flag) return ;
dfn[x]=low[x]=++cnt;
sta[++top]=x;
rep_e(i,x){
int v=to[i];
if(vis[v]) flag=1;
if(!dfn[v]){
tarjan(v);
low[x]=min(low[x],low[v]);
}
else if(!belong[v]) low[x]=min(low[x],low[v]);
}
if(dfn[x]==low[x]){
qiang++;
if(qiang>1) {flag=1; return ;}
while(1){
int u=sta[top--];
belong[u]=qiang;
if(u==x) break;
}
}
vis[x]=1;
}
void init(){
cnt=0; flag=0; top=0;qiang=0;
rep(i,1,n) {
head[i]=-1,vis[i]=0;
dfn[i]=low[i]=0;
belong[i]=0;
}
}
int main(){
int T; scanf("%d",&T);
while(T--){
scanf("%d",&n);
init();
int x,y;
while(~scanf("%d%d",&x,&y)){
if(x==0&&y==0) break;
x++,y++;
add(x,y);
}
rep(i,1,n){
if(!dfn[i]) tarjan(i);
}
if(flag) {
printf("NO\n"); continue;
}
rep(u,1,n){
int nu=0;
rep_e(i,u){
int v=to[i];
if(low[v]>dfn[u]) flag=1;
if(low[v]<dfn[u]) nu++;
}
if(nu>=2) flag=1;
}
if(flag) printf("NO\n");
else printf("YES\n");
}
return 0;
}