题目大意:有n个点的有向图,对于其中任意两点u,v是否都能使u能走到v或者v能走到u(注意是或者!!!),如果是输出Yes,否则输出No。
思路:裸的强联通。学了几天没写强联通的题,手又生了。对于图中的每个环,它们之间都能够相互到达,所以把它们缩成一个点(联通块)。然后判断缩点后的图是否为一条单向的链,如果是,那么条件肯定成立。否则不成立。
#include<cstdio>
#include<cstring>
#define MAXN 1005
#define Min(a,b) a<b?a:b
using namespace std;
struct T
{
int v;
int next;
}edge[6005];
int cnt;
int head[MAXN];
void add_edge(int u,int v)
{
edge[cnt].v = v;
edge[cnt].next = head[u];
head[u] = cnt++;
}
int n,m;
int dcnt;
int low[MAXN],dfn[MAXN];
int stack[MAXN],tot;
bool instack[MAXN];
int cut_e,belong[MAXN];
int in[MAXN];
bool arr[MAXN][MAXN],vis[MAXN];
void dfs(int u)
{
low[u] = dfn[u] = ++dcnt;
stack[++tot] = u;
instack[u] = true;
for(int i = head[u]; i != -1; i = edge[i].next)
{
int v = edge[i].v;
if(!dfn[v])
{
dfs(v);
low[u] = Min(low[u],low[v]);
}
else if(instack[v])
{
low[u] = Min(low[u],dfn[v]);
}
}
if(low[u] == dfn[u])//找到一个联通块
{
++cut_e;
do
{
belong[stack[tot]] = cut_e;
instack[stack[tot]] = false;
tot--;
}while(stack[tot+1] != u);
}
}
int ans;
void count(int u)
{
vis[u] = 1;
++ans;
for(int i = 1; i <= cut_e; i++)
{
if(arr[u][i]&&!vis[i])//打成!vis[u],立碑纪念
{
count(i);
return;
}
}
}
int main()
{
int T;
scanf("%d",&T);
while(T--)
{
memset(edge,0,sizeof edge);
memset(low,0,sizeof low);
memset(dfn,0,sizeof dfn);
memset(head,-1,sizeof head);
memset(belong,0,sizeof belong);
memset(in,0,sizeof in);
memset(vis,0,sizeof vis);
memset(arr,0,sizeof arr);
cnt = dcnt = 0;
tot = 0;
cut_e = 0;
scanf("%d%d",&n,&m);
for(int i = 1; i <= m; i++)
{
int a,b;
scanf("%d%d",&a,&b);
add_edge(a,b);
}
for(int i = 1; i <= n; i++)
if(!dfn[i])
dfs(i);
for(int u = 1; u <= n; u++)//缩点,构建新图
{
for(int i = head[u]; i != -1; i = edge[i].next)
{
int v = edge[i].v;
if(belong[u] != belong[v]&&!arr[belong[u]][belong[v]])//打成!arr[u][v],立碑纪念
{
arr[belong[u]][belong[v]] = 1;//新图连边
in[belong[v]]++;//入度加1
}
}
}
ans = 0;
for(int i = 1; i <= cut_e; i++)//判断是否为一条链,也就是从一个入度为0的点走看是否能走完所有联通块
{
if(in[i] == 0)
{
count(i);
break;
}
}
if(ans == cut_e) printf("Yes\n");
else printf("No\n");
}
}