POJ - 2762(强连通模板缩点+拓扑模板)

Going from u to v or from v to u?

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.

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

题意:一个有向图中有n个点m条边,问是否任意两点u,v之间可以u-v或者v->u。

思路:学过强连通可以知道,在一个强连通中任意两点都可以满足题目条件。如果图中有多个强连通分量,则需要在强连通分量之间建图,再拓扑排序判断图中是否同时存在一个入度为零的两点,如果存在那么这两点肯定不满足题目条件。

#include <iostream>
#include <fstream>
#include <cstdio>
#include <cstring>
#include <queue>
#include <stack>
#include <vector>
#include <map>
#include <set>
#include <cmath>
#include <algorithm>
#include <functional>
#define inf 0X3f3f3f3f
using namespace std;
typedef long long ll;
const int MAXN=1e5+10;
const int MAX=12000+10;
const double eps=1e-6;

int du[MAX];
//强连通模板
int book_stack[MAX];    //标记入栈
int dfn[MAX];           //储存u被搜索的次序
int low[MAX];           //储存u或u的子树能追溯到的最早的栈中的节点次序
int first[MAX];       //链接表
int belong[MAX];        //标记节点属于哪个强连通分量
int n,m,scc,Time,num;  //n个节点,m条边,scc强连通分量数量,time次序
stack<int>q;            //栈储存节点
struct EDGE{
    int v;
    int next;
}edge[MAX];   //      链接表
struct NODE{
    int u,v;
}node[MAX];

void init(){        //初始化
    scc=0;
    Time=1;
    num=0;
    memset(du,0,sizeof(du));
    memset(first,-1,sizeof(first));
    memset(book_stack,0,sizeof(book_stack));
    memset(dfn,0,sizeof(dfn));
    memset(low,0,sizeof(low));
}

void addedge(int u,int v){     //建立链接表
    edge[num].v=v;
    edge[num].next=first[u];
    first[u]=num++;
}

void tarjan(int u){     //tarjan核心算法
    dfn[u]=low[u]=Time++;   //为u设定次序和初始化low
    q.push(u);              //u入栈
    book_stack[u]=1;        //标记入栈
    for(int i=first[u];i!=-1;i=edge[i].next){   //遍历每一条边
        int temp=edge[i].v;
        if(!dfn[temp]){         //如果v没有被访问过
            tarjan(temp);       //继续从v找
            low[u]=min(low[u],low[temp]);   //判断向后扩展强连通分量
        }
        else if(book_stack[temp])      //如果v被访问过并且在栈中
            low[u]=min(low[u],dfn[temp]);   //判断向前合并强连通分量
    }
    if(dfn[u]==low[u]){         //找到强连通分量的根
        int v;
        do{
            v=q.top();q.pop();
            book_stack[v]=0;
            belong[v]=scc;
        }
        while(u!=v);
        scc++;
    }
}

void get_scc(){
    for(int i=1;i<=n;i++)
        if(!dfn[i])
            tarjan(i);
}

void tsort(){
    int cnt=0;
    queue<int>q;
    for(int i=n+1;i<=n+scc;i++)
        if(du[i]==0){
            cnt++;
            q.push(i);
        }
    if(cnt>1){
        cout<<"No"<<endl;
        return ;
    }
    while(q.size()){
        int u=q.front();q.pop();
        cnt=0;
        for(int i=first[u];i!=-1;i=edge[i].next){
            int v=edge[i].v;
            du[v]--;
            if(du[v]==0){
                q.push(v);
                cnt++;
            }
        }
        if(cnt>1){
            cout<<"No"<<endl;
            return ;
        }
    }
    cout<<"Yes"<<endl;
    return ;
}


int main(){
    #ifdef ONLINE_JUDGE
    #else
    freopen("in.txt","r",stdin);
    //freopen("out.txt","w",stdout);
    #endif
    int T;
    cin>>T;
    while(T--){
        cin>>n>>m;
        init();
        for(int i=1;i<=m;i++){
            scanf("%d%d",&node[i].u,&node[i].v);
            addedge(node[i].u,node[i].v);
        }
        get_scc();
        for(int i=1;i<=n;i++)
            belong[i]++;
        if(scc==1){
            cout<<"Yes"<<endl;
            continue;
        }
        for(int i=1;i<=m;i++){
            int u=belong[node[i].u]+n;  //额外建图
            int v=belong[node[i].v]+n;
            if(u!=v){
                addedge(u,v);
                du[v]++;
            }
        }
        tsort();
    }
    return 0;
}
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值