【POJ】2762 Going from u to v or from v to u? 强连通+最长路

Going from u to v or from v to u?
Time Limit: 2000MS
Memory Limit: 65536K
Total Submissions: 14052
Accepted: 3676

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.

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

Source

POJ Monthly--2006.02.26,zgl & twb

传送门:【POJ】2762 Going from u to v or from v to u?

题目大意:给你一副n个点的有向图,问是否能够任选两个点x、y使得x能到达y或者y能到达x。

题目分析:强连通缩点求点权最长路,如果最长路长度等于n则说明任选两点都能使得存在其中一个到达另一个。

代码如下:
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std ;

#define REPF( i , a , b ) for ( int i = a ; i <= b ; ++ i )
#define REP( i , n ) for ( int i = 0 ; i < n ; ++ i )
#define clear( a , x ) memset ( a , x , sizeof a )

const int MAXN = 1005 ;
const int MAXE = 10000 ;
const int MAXQ = 10000 ;
const int INF = 0x3f3f3f3f ;

struct Edge {
	int v , n ;
	Edge ( int var = 0 , int next = 0 ) : v(var) , n(next) {}
} ;

struct SCC {
	Edge edge[MAXE] ;
	int adj[MAXN] , cntE ;
	int Dfn[MAXN] , Low[MAXN] , dfs_clock ;
	int scc[MAXN] , scc_cnt ;
	int S[MAXN] , top ;
	bool ins[MAXN] ;
	
	void init () {
		top = 0 ;
		cntE = 0 ;
		scc_cnt = 0 ;
		dfs_clock = 0 ;
		clear ( ins , 0 ) ;
		clear ( Dfn , 0 ) ;
		clear ( adj , -1 ) ;
	}
	
	void addedge ( int u , int v ) {
		edge[cntE] = Edge ( v , adj[u] ) ;
		adj[u] = cntE ++ ;
	}
	
	void Tarjan ( int u ) {
		Dfn[u] = Low[u] = ++ dfs_clock ;
		S[top ++] = u ;
		ins[u] = 1 ;
		for ( int i = adj[u] ; ~i ; i = edge[i].n ) {
			int v = edge[i].v ;
			if ( !Dfn[v] ) {
				Tarjan ( v ) ;
				Low[u] = min ( Low[u] , Low[v] ) ;
			}
			else if ( ins[v] )
				Low[u] = min ( Low[u] , Dfn[v] ) ;
		}
		if ( Low[u] == Dfn[u] ) {
			++ scc_cnt ;
			while ( 1 ) {
				int v = S[-- top] ;
				ins[v] = 0 ;
				scc[v] = scc_cnt ;
				if ( v == u )
					break ;
			}
		}
	}
	
	void find_scc ( int n ) {
		REPF ( i , 1 , n )
			if ( !Dfn[i] )
				Tarjan ( i ) ;
	}
} ;

struct DAG {
	Edge edge[MAXE] ;
	int adj[MAXN] , cntE ;
	int in[MAXN] ;
	int d[MAXN] ;
	int num[MAXN] ;
	int Q[MAXQ] ;
	int head , tail ;
	
	void init () {
		cntE = 0 ;
		clear ( in , 0 ) ;
		clear ( num , 0 ) ;
		clear ( adj , -1 ) ;
	}
	
	void addedge ( int u , int v ) {
		edge[cntE] = Edge ( v , adj[u] ) ;
		adj[u] = cntE ++ ;
	}
	
	int topo ( int n , int tot ) {
		head = tail = 0 ;
		clear ( d , 0 ) ;
		REPF ( i , 1 , n )
			if ( !in[i] )
				d[Q[tail ++] = i] = num[i] ;
		while ( head != tail ) {
			int u = Q[head ++] ;
			for ( int i = adj[u] ; ~i ; i = edge[i].n ) {
				int v = edge[i].v ;
				if ( d[v] < d[u] + num[v] )
					d[v] = d[u] + num[v] ;
				if ( 0 == ( -- in[v] ) )
					Q[tail ++] = v ;
			}
		}
		REPF ( i , 1 , n ) {
			if ( d[i] == tot )
				return 1 ;
		}
		return 0 ;
	}
} ;	

SCC C ;
DAG D ;

int read ( int &x ) {
	x = 0 ;
	char c = ' ' ;
	while ( c < '0' || c > '9' )
		c = getchar () ;
	if ( c == -1 )
		return 0 ;
	while ( c >= '0' && c <= '9' ) {
		x = x * 10 + c - '0' ;
		c = getchar () ;
	}
	return 1 ;
}

void work () {
	int n , m ;
	int u , v ;
	C.init () ;
	read ( n ) , read ( m ) ;
	//scanf ( "%d%d" , &n , &m ) ;
	while ( m -- ) {
		read ( u ) , read ( v ) ;
		//scanf ( "%d%d" , &u , &v ) ;
		C.addedge ( u , v ) ;
	}
	C.find_scc ( n ) ;
	if ( C.scc_cnt == 1 ) {
		printf ( "Yes\n" ) ;
		return ;
	}
	D.init () ;
	REPF ( u , 1 , n )
		for ( int i = C.adj[u] ; ~i ; i = C.edge[i].n ) {
			int v = C.edge[i].v ;
			if ( C.scc[u] != C.scc[v] ) {
				D.addedge ( C.scc[u] , C.scc[v] ) ;
				++ D.in[C.scc[v]] ;
			}
		}
	REPF ( i , 1 , n )
		++ D.num[C.scc[i]] ;
	if ( D.topo ( C.scc_cnt , n ) )
		printf ( "Yes\n" ) ;
	else
		printf ( "No\n" ) ;
}

int main () {
	int T ;
	scanf ( "%d" , &T ) ;
	while ( T -- )
		work () ;
	return 0 ;
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值