POJ2762
题意:给出n个点,对于每个点,如果任意选择两点s,e,都满足s可以到达e或者e可以到达s,则输出Yes,否则输出No。
用了白书里的模板
参考http://blog.csdn.net/acceptedxukai/article/details/6961171
解决思路是:首先求出原图G的强连通分量并且缩点,求出缩点后的图mat,并且求出缩点后所有顶点的入度in[]。
这时我们思考下,如果原图G要是半连通的,那么缩点后的图mat必须要连通,这是基础的前提,不然原图都是不连通的,这时只要判断mat中顶点是否只有一个入度为0的点,否则图G不是半连通的。此外,mat就是一棵树,入度为0的顶点就是根,如果这个树不是一条链,那么图G也不是半连通的,不是链就说明有分叉,两个分叉之间是不能到达的,那么如何判断是否有分叉呢?答案是拓扑排序,如果排序到某个节点后,剩下的顺序不能确定,就说明出现了分叉。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#define N 10010
using namespace std;
vector<int>S;
vector<int>G[N],G2[N],mat[N];
bool vis[N];
int sccno[N],ru[N];
int n,m;
int scc_cnt;
void dfs1(int u)
{
if(vis[u])return;
vis[u]=1;
for(int i=0;i<G[u].size();i++)dfs1(G[u][i]);
S.push_back(u);
}
void dfs2(int u)
{
if(sccno[u])return;
// printf(" %d\n",u);
sccno[u]=scc_cnt;//属于哪个连通分量
for(int i=0;i<G2[u].size();i++)dfs2(G2[u][i]);
}
void find_scc()
{
int i;
scc_cnt=0;
S.clear();
memset(sccno,0,sizeof(sccno));
memset(vis,0,sizeof(vis));
for(i=1;i<=n;i++)if(!vis[i])dfs1(i);
for(i=S.size()-1;i>=0;i--)
{
if(!sccno[S[i]])
{
scc_cnt++;
// printf("---- %d %d\n",S[i],scc_cnt);
dfs2(S[i]);
}
}
}
void topsort()
{
int i,j,u,v;
for(i=1;i<=n;i++)
{
for(j=0;j<G[i].size();j++)
{
u=i;
v=G[i][j];
if(sccno[v]!=sccno[u])
{
mat[sccno[u]].push_back(v);
ru[sccno[v]]++;
}
}
}
// for(i=1;i<=scc_cnt;i++)printf(" %d %d\n",i,ru[i]);
int ans,num,p;
ans=0;
while(1)
{
num=0;
for(i=1;i<=scc_cnt;i++)
{
if(ru[i]==0){
num++;
p=i;
}
}
// printf("num=%d p= %d\n",num,p);
if(num!=1)break;
ru[p]=-1;
ans++;
for(i=0;i<mat[p].size();i++)
{
ru[sccno[mat[p][i]]]--;
}
}
// printf("ans=%d\n",ans);
if(ans==scc_cnt)printf("Yes\n");
else printf("No\n");
}
int main()
{
int t,a,b,i;
scanf("%d",&t);
while(t--)
{
scanf("%d%d",&n,&m);
for(i=1;i<=n;i++)
{
G[i].clear();
G2[i].clear();
mat[i].clear();
ru[i]=0;
}
while(m--)
{
scanf("%d%d",&a,&b);
G[a].push_back(b);
G2[b].push_back(a);
}
find_scc();
topsort();
}
return 0;
}