此题数据过水,第一次A时其实代码是错的,但是AC了。。。。。。下面有几组比较苛刻的数据(但不保证情况覆盖完全)。
4
0 1
1 2
2 0
2 3
3 0
0 0
答案应该是NO。
4
0 1
1 2
2 3
3 2
2 0
0 0
4
0 1
1 2
2 0
2 3
3 2
0 0
这两组是相同的图,加点的顺序不同,用来判断代码正确性。应该全为YES。
4
0 1
1 2
2 3
3 0
0 2
0 0
4
0 2
2 3
3 0
0 1
1 2
0 0
这两组也是相同的图,应该全为NO。
Cactus图有三种条件:
1.为强连通图。
2.没有横叉边。
3.每个点u其子节点v,low[v]<dfn[u]的个数和该点后向边的个数加起来不能超过两个。
其中展开第三个条件形容的情况就是:
①:
xxxxxx->u->xxxx
↑--↑---------xxxx(low[v]<dfn[u]总数等于2的情况)。
②:
xxxxxx->u->xxxx
↑-----u
↑-----------xxxx(low[v]<dfn[u]个数+后向边个数等于2的情况)。
③:
xxxxxx->u->xxxx
↑--↑------u(多条后向边的情况)。
于是附代码:
#include <cstdio>
#include <cstring>
#include <vector>
#define NV 20001
#define NE 50001
using namespace std;
const int W=0;
const int G=1;
const int B=2;
vector<int>dag[NE];
int col[NV],dex;
int dfn[NV],low[NV];
int n;
int min(int a,int b)
{
if(a<b)return a;
return b;
}
//染色法,W为该点还未访问,B为该点的所有子树和其自身已经访问完毕,G为正在访问该点。
bool strong_tarjan(int u)
{
low[u]=dfn[u]=dex++;//时间戳
col[u]=G;//开始访问
int cnt=0;
for(int i=0;i<(int)dag[u].size();i++){
int v=dag[u][i];
if(col[v]==B)return false;//如果有横叉边就不满足条件。
if(col[v]==W){
if(!strong_tarjan(v))return false;
if(low[v]>dfn[u])return false;//后面的low[v]>dfn[u],几乎能满足割边的条件了,必然不是连通图。
if(low[v]<dfn[u])cnt++;//统计个数
if(cnt>=2)return false;//条件三
low[u]=min(low[u],low[v]);
}
else if(col[v]==G){
low[u]=min(dfn[v],low[u]);
cnt++;//统计个数
if(cnt>=2)return false;//条件三
}
}
col[u]=B;
return true;
}
void init()
{
dex=0;
for(int i=0;i<=n;i++){
col[i]=W;
dag[i].clear();
dfn[i]=-1;
}
}
int main()
{
int cas;
scanf("%d",&cas);
while(cas--){
scanf("%d",&n);
int p1,p2;
init();
while(scanf("%d%d",&p1,&p2),p1||p2){
dag[p1].push_back(p2);
}
bool flag=strong_tarjan(0);
if(flag){
for(int i=0;i<n;i++){
if(dfn[i]==-1){
flag=false;//如果还有没访问到的点说明改图不是强连通图(注意只是不是强连通,图可能是连通的但是因为有些点不为强连通所以一次tarjan不能全搜到)。
break;
}
}
}
if(flag)
printf("YES\n");
else
printf("NO\n");
}
return 0;
}