Going from u to v or from v to u?
Time Limit: 2000MS | Memory Limit: 65536K | |
Total Submissions: 12692 | Accepted: 3271 |
Description
In order to make their sons brave, Jiajia and Wind take them to a big cave. The cave has n rooms, and one-way corridors connecting some rooms. Each time, Wind choose two rooms x and y, and ask one of their little sons go from one to the other. The son can either go from x to y, or from y to x. Wind promised that her tasks are all possible, but she actually doesn't know how to decide if a task is possible. To make her life easier, Jiajia decided to choose a cave in which every pair of rooms is a possible task. Given a cave, can you tell Jiajia whether Wind can randomly choose two rooms without worrying about anything?
Input
The first line contains a single integer T, the number of test cases. And followed T cases.
The first line for each case contains two integers n, m(0 < n < 1001,m < 6000), the number of rooms and corridors in the cave. The next m lines each contains two integers u and v, indicating that there is a corridor connecting room u and room v directly.
The first line for each case contains two integers n, m(0 < n < 1001,m < 6000), the number of rooms and corridors in the cave. The next m lines each contains two integers u and v, indicating that there is a corridor connecting room u and room v directly.
Output
The output should contain T lines. Write 'Yes' if the cave has the property stated above, or 'No' otherwise.
Sample Input
1 3 3 1 2 2 3 3 1
Sample Output
Yes
分析:原图中有m条单向边,求任意给两个点,是否能使其中一个点到达另一个点(弱连通)
先用强连通缩点,将在同一个强连通分量里的缩成一个点;
然后建立新图,此时图中只有单向边(即若有u到v,则不会有v到u);
最后利用拓扑,验证新图是否是单链(极端情况只有一个点,但不影响
#include<cstdio>
#include<cstring>
#include<vector>
using namespace std;
const int N=1002;
int dfn[N],low[N],belong[N],cnt[N],in[N],out[N],stack[N];
//dfn 深度优先搜索访问次序
//low 能追溯到的最早的次序
//belong 所属强连通分量
//cnt 各个强连通分量所含节点数(本题可不用)
//in 新图节点入度数
//out 新图节点入度数(本题可不用)
//stack 栈模拟数组
int scc,index,top,n,ci,co;
//scc 强连通分量数
//index 索引号
//top 模拟栈顶
//n 原图节点总数
//ci 新图入度为0的节点总数(本题可不用)
//co 新图出度为0的节点总数(本题可不用)
bool instack[N];
//instack 标记是否在栈内
vector<int> g1[N],g2[N];
//g1 原图;g2 新图
int min(int a,int b){return a<b?a:b;}
void init(){
scc=index=top=0;
memset(dfn,-1,sizeof(dfn));
memset(instack,0,sizeof(instack));
memset(cnt,0,sizeof(cnt));
for(int i=0;i<=n;i++)g1[i].clear(),g2[i].clear();
}
void tarjan(int x){
int i,v;
dfn[x]=low[x]=++index;//为节点x设定次序编号和low初值
stack[++top]=x,instack[x]=1;//将节点x压入栈中
for(i=0;i<g1[x].size();i++){ //枚举每一条边
v=g1[x][i];
if(dfn[v]==-1){//如果节点v未被访问过
tarjan(v);//继续向下找
low[x]=min(low[x],low[v]);
}else if(instack[v]){//如果节点v还在栈内
low[x]=min(low[x],dfn[v]);
}
}
if(low[x]==dfn[x]){//如果节点x是强连通分量的根
scc++;
while(1){
v=stack[top--],instack[v]=0;
belong[v]=scc;
cnt[scc]++;
if(v==x)break;
}
}
}
void CreateMap(){//建立新图
int i,j,S,u,v;
memset(in,0,sizeof(in));
memset(out,0,sizeof(out));
for(i=1;i<=n;i++){
S=g1[i].size();
for(j=0;j<S;j++){
u=belong[i],v=belong[g1[i][j]];
if(u!=v)in[v]++,out[u]++,g2[u].push_back(v);
}
}
ci=co=0;
for(i=1;i<=scc;i++){
if(!in[i])ci++;
if(!out[i])co++;
}
}
bool TopSort(){//拓扑
int i,S,u,v;
top=0;
for(i=1;i<=scc;i++)if(in[i]==0)stack[++top]=i;
while(top){
if(top>1)return 0;
u=stack[top--];
S=g2[u].size();
for(i=0;i<S;i++){
v=g2[u][i];
if((--in[v])==0)stack[++top]=v;
}
}
return 1;
}
int main(){
int T,i,m,a,b;
scanf("%d",&T);
while(T--){
scanf("%d%d",&n,&m);
init();
while(m--){
scanf("%d%d",&a,&b);
g1[a].push_back(b);
}
for(i=1;i<=n;i++)if(dfn[i]==-1)tarjan(i);
CreateMap();
puts(TopSort()?"Yes":"No");
}
return 0;
}