题干:

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

题目大意:

问一个图是否是单连通的。

解题报告:

先对全图求一次SCC,可以知道每个SCC内的点都是单连通的,那么把每个SCC缩点构建出DAG之后再判断这个DAG是否单连通即可。方法不唯一:可以是拓扑排序,看他每次是否只有一个活跃点就可以了,如果不是一个,那肯定不符合要求。

第二种方法:是DAG动规找出最长链,如果最长链上的点个数等于SCC个数,那么DAG单连通。(因为如果最长链都不能覆盖所有点,那么必定有两个点之间是无法到达的)

 

刚开始以为直接判断是否是一条链就行了,后来3 -> 1、 1 -> 2、3 -> 2这组数据就Hack掉了,,本应该是Yes,但是这样肯定就是No了。然后想判一个欧拉通路,虽然可以AC,但是有数据是过不了的。(数据水了)

AC代码:

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<queue>
#include<map>
#include<vector>
#include<set>
#include<string>
#include<cmath>
#include<cstring>
#define F first
#define S second
#define ll long long
#define pb push_back
#define pm make_pair
using namespace std;
typedef pair<int,int> PII;
const int MAX = 1000 + 5;
struct Edge {
	int fr,to,ne;
} e[6005],E[6005];
vector<int> vv[MAX];
int head[MAX],HEAD[MAX],tot,TOT;
bool ok[MAX][MAX];
void add(int u,int v) {
	e[++tot].to = v;
	e[tot].fr = u;
	e[tot].ne = head[u];
	head[u] = tot;
}
int n,m;
int DFN[MAX],LOW[MAX],scc,clk,index,sk[MAX],vis[MAX],col[MAX],in[MAX],out[MAX]; 
void Tarjan(int x) {
	DFN[x] = LOW[x] = ++clk;
	vis[x]=1;
	sk[++index] = x;
	for(int i = head[x]; ~i; i = e[i].ne) {
		int v = e[i].to;
		if(DFN[v] == 0) {
			Tarjan(v);
			LOW[x] = min(LOW[x],LOW[v]);
		}
		else if(vis[v]) {
			LOW[x] = min(LOW[x],DFN[v]);
		}		
	}
	if(LOW[x] == DFN[x]) {
		scc++;
		while(1) {
			int tmp = sk[index];index--;
			vis[tmp]=0;
			col[tmp] = scc;
			if(tmp == x) break;
		}				
	}	
}
void init() {
	scc=clk=tot=TOT=index=0;
	for(int i = 1; i<=n; i++) {
		vv[i].clear();
		head[i] = HEAD[i] = -1;
		vis[i] = DFN[i] = LOW[i] = in[i] = out[i] = 0;
	}
	for(int i = 1; i<=n; i++) {
		for(int j = 1; j<=n; j++) ok[i][j]=0;
	}
}
bool topu() {
	queue<int> q;
	for(int i = 1; i<=scc; i++) {
		if(in[i] == 0)  q.push(i);
	}
	int cnt = 0;
	while(!q.empty()) {
		if(q.size() > 1) return 0 ;
		int cur = q.front();q.pop();
		cnt++;
		int up = vv[cur].size();
		for(int i = 0; i<up; i++) {
			int v = vv[cur][i];
			in[v]--;
			if(in[v] == 0) q.push(v);
		}		
	}
	return cnt == scc;
}
int main()
{
	int t;
	cin>>t;
	while(t--) {
		scanf("%d%d",&n,&m);		
		init();
		for(int a,b,i = 1; i<=m; i++) {
			scanf("%d%d",&a,&b);
			add(a,b);
		}
		for(int i = 1; i<=n; i++) {
			if(DFN[i] == 0) Tarjan(i);
		}
		for(int a,b,i = 1; i<=tot; i++) {
			a=col[e[i].fr],b=col[e[i].to];
			if(a==b) continue;
			if(ok[a][b]) continue;
			ok[a][b]=1;
			in[b]++;
			out[a]++;
			vv[a].pb(b);
		}
		bool flag = topu();
		if(flag) puts("Yes");
		else puts("No");
	}


	return 0 ;
}

AC代码:(树形dp)

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<queue>
#include<map>
#include<vector>
#include<set>
#include<string>
#include<cmath>
#include<cstring>
#define F first
#define S second
#define ll long long
#define pb push_back
#define pm make_pair
using namespace std;
typedef pair<int,int> PII;
const int MAX = 1000 + 5;
struct Edge {
	int fr,to,ne;
} e[6005],E[6005];
vector<int> vv[MAX];
int head[MAX],HEAD[MAX],tot,TOT;
bool ok[MAX][MAX];
void add(int u,int v) {
	e[++tot].to = v;
	e[tot].fr = u;
	e[tot].ne = head[u];
	head[u] = tot;
}
int n,m;
int dp[MAX];
int DFN[MAX],LOW[MAX],scc,clk,index,sk[MAX],vis[MAX],col[MAX],in[MAX],out[MAX]; 
void Tarjan(int x) {
	DFN[x] = LOW[x] = ++clk;
	vis[x]=1;
	sk[++index] = x;
	for(int i = head[x]; ~i; i = e[i].ne) {
		int v = e[i].to;
		if(DFN[v] == 0) {
			Tarjan(v);
			LOW[x] = min(LOW[x],LOW[v]);
		}
		else if(vis[v]) {
			LOW[x] = min(LOW[x],DFN[v]);
		}		
	}
	if(LOW[x] == DFN[x]) {
		scc++;
		while(1) {
			int tmp = sk[index];index--;
			vis[tmp]=0;
			col[tmp] = scc;
			if(tmp == x) break;
		}				
	}	
}
void init() {
	scc=clk=tot=TOT=index=0;
	for(int i = 1; i<=n; i++) {
		vv[i].clear();
		head[i] = HEAD[i] = -1;
		vis[i] = DFN[i] = LOW[i] = in[i] = out[i] = dp[i] = 0;
	}
	for(int i = 1; i<=n; i++) {
		for(int j = 1; j<=n; j++) ok[i][j]=0;
	}
}
int dfs(int cur){
	if(dp[cur])return dp[cur];
	int num=0;
	int up = vv[cur].size();
	for(int i = 0; i<up; i++) {
		int v = vv[cur][i];
		num=max(num,dfs(v));
	}
	return dp[cur]=num+1;
}
int main()
{
	int t;
	cin>>t;
	while(t--) {
		scanf("%d%d",&n,&m);		
		init();
		for(int a,b,i = 1; i<=m; i++) {
			scanf("%d%d",&a,&b);
			add(a,b);
		}
		for(int i = 1; i<=n; i++) {
			if(DFN[i] == 0) Tarjan(i);
		}
		for(int a,b,i = 1; i<=tot; i++) {
			a=col[e[i].fr],b=col[e[i].to];
			if(a==b) continue;
			if(ok[a][b]) continue;
			ok[a][b]=1;
			in[b]++;
			out[a]++;
			vv[a].pb(b);
		}
		int flag = 0;
		for(int i = 1; i<=n; i++){
			if(dfs(LOW[i]) == scc){flag=true;break;}//或者dfs(i)也可以ac
		}
		if(flag) puts("Yes");
		else puts("No");
	}
	return 0 ;
}

这里送上一种虽然错误但是可以AC的代码,引以为戒。

AC代码:

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<queue>
#include<map>
#include<vector>
#include<set>
#include<string>
#include<cmath>
#include<cstring>
#define F first
#define S second
#define ll long long
#define pb push_back
#define pm make_pair
using namespace std;
typedef pair<int,int> PII;
const int MAX = 1000 + 5;
struct Edge {
	int fr,to,ne;
} e[6005],E[6005];
int f[MAX];
int getf(int v) {
	return f[v] == v ? v : f[v] = getf(f[v]); 
}
void merge(int u,int v) {
	int t1 = getf(u),t2 = getf(v);
	f[t2] = t1;
}
int head[MAX],HEAD[MAX],tot,TOT;
bool ok[MAX][MAX];
void add(int u,int v) {
	e[++tot].to = v;
	e[tot].fr = u;
	e[tot].ne = head[u];
	head[u] = tot;
}
int n,m;
int DFN[MAX],LOW[MAX],scc,clk,index,sk[MAX],vis[MAX],col[MAX],in[MAX],out[MAX]; 
void Tarjan(int x) {
	DFN[x] = LOW[x] = ++clk;
	vis[x]=1;
	sk[++index] = x;
	for(int i = head[x]; ~i; i = e[i].ne) {
		int v = e[i].to;
		if(DFN[v] == 0) {
			Tarjan(v);
			LOW[x] = min(LOW[x],LOW[v]);
		}
		else if(vis[v]) {
			LOW[x] = min(LOW[x],DFN[v]);
		}		
	}
	if(LOW[x] == DFN[x]) {
		scc++;
		while(1) {
			int tmp = sk[index];index--;
			vis[tmp]=0;
			col[tmp] = scc;
			if(tmp == x) break;
		}				
	}	
}
void init() {
	scc=clk=tot=TOT=index=0;
	for(int i = 1; i<=n; i++) {
		head[i] = HEAD[i] = -1;
		f[i]=i;
		vis[i] = DFN[i] = LOW[i] = in[i] = out[i] = 0;
	}
	for(int i = 1; i<=n; i++) {
		for(int j = 1; j<=n; j++) ok[i][j]=0;
	}
}
int main()
{
	int t;
	cin>>t;
	while(t--) {
		scanf("%d%d",&n,&m);		
		init();
		for(int a,b,i = 1; i<=m; i++) {
			scanf("%d%d",&a,&b);
			add(a,b);
		}
		for(int i = 1; i<=n; i++) {
			if(DFN[i] == 0) Tarjan(i);
		}
		for(int a,b,i = 1; i<=tot; i++) {
			a=col[e[i].fr],b=col[e[i].to];
			if(a==b) continue;
			if(ok[a][b]) continue;
			ok[a][b]=1;
			merge(a,b);
			in[b]++;
			out[a]++;
		}
		int ans = 0;
		int flag = 0,ru=0,chu=0;
		for(int i = 1; i<=scc; i++) {
			if(f[i] == i) ans++;
			if(in[i]==out[i]) continue;
			if(in[i]-out[i]==1) ru++;
			else if(out[i]-in[i]==1) chu++;
			else {flag=1; break;}
		}	
		if(ans > 1 || flag == 1 || ru!=chu || ru>1 || chu>1  ) puts("No");
		else puts("Yes");
	}


	return 0 ;
}

但是其实这样是不对的:

1
3 3
3 1
1 2
3 2

这组样例就过不了,所以不能简单判断一个有向图欧拉通路。