虽然本题是求单连通的,但是我们需要先求强连通分量,因为,强连通分量中存在双向路径,因此可以缩点,缩点后就好处理多了。
如果要满足题意,缩点后的树必须是一条链,而且所有边的方向都是一样的,如果出现分支,很容易证明会出现不可到达的一对点。
那么剩下的就是求最长链的顶点数是否等于强连通分量的个数了。
那么就可以使用拓扑排序,或者直接DFS。
拓扑排序中,不能出现同时有两个点的入度为0。
DFS从入度为0的顶点开始搜,是求出深搜的层数,因为单链的话层数是等于顶点数的
下面贴出用Kosaraju算法实现的代码
/* ID: sdj22251 PROG: subset LANG: C++ */ #include <iostream> #include <vector> #include <list> #include <map> #include <set> #include <deque> #include <queue> #include <stack> #include <bitset> #include <algorithm> #include <functional> #include <numeric> #include <utility> #include <sstream> #include <iomanip> #include <cstdio> #include <cmath> #include <cstdlib> #include <cctype> #include <string> #include <cstring> #include <cmath> #include <ctime> #define LOCA #define MAXN 10005 #define INF 100000000 #define eps 1e-7 using namespace std; struct Edge { int v, next; }edge[10 * MAXN], revedge[10 * MAXN]; int head[MAXN], revhead[MAXN], e, visited[MAXN]; int order[MAXN], cnt, id[MAXN], tmp; int uu[5 * MAXN], vv[5 * MAXN], in[MAXN]; int n, m; void init() { e = 0; memset(head, -1, sizeof(head)); memset(revhead, -1, sizeof(revhead)); memset(in, 0 , sizeof(in)); } void insert(const int &x, const int &y) { edge[e].v = y; edge[e].next = head[x]; head[x] = e; revedge[e].v = x; revedge[e].next = revhead[y]; revhead[y] = e; e++; } void readdata() { for(int i = 0; i < m; i++) { scanf("%d%d", &uu[i], &vv[i]); insert(uu[i], vv[i]); } } void dfs(int u) //搜索原图 { visited[u] = 1; for(int i = head[u]; i != -1; i = edge[i].next) { int v = edge[i].v; if(!visited[v]) dfs(v); } order[cnt++] = u; } void dfs_rev(int u) //搜索逆图 { visited[u] = 1; id[u] = cnt; for(int i = revhead[u]; i != -1; i = revedge[i].next) { int v = revedge[i].v; if(!visited[v]) dfs_rev(v); } } void search(int u, int deep) //缩点后的搜索 { visited[u] = 1; tmp = max(tmp, deep); for(int i = head[u]; i != -1; i = edge[i].next) { int v = edge[i].v; if(!visited[v]) { if(id[u] == id[v]) search(v, deep); else search(v, deep + 1); } } } void Kosaraju() { init(); readdata(); memset(visited, 0, sizeof(visited)); cnt = 0; for(int i = 1; i <= n; i++) { if(!visited[i]) dfs(i); } memset(visited, 0, sizeof(visited)); cnt = 0; for(int i = n - 1; i >= 0; i--) { if(!visited[order[i]]) { cnt++; dfs_rev(order[i]); } } for(int i = 0; i < m; i++) { int u = id[uu[i]]; int v = id[vv[i]]; if(u != v) in[v]++; } tmp = 0; memset(visited, 0, sizeof(visited)); for(int i = 1; i <= n; i++) { if(in[id[i]] == 0) { search(i, 1); break; } } } int main() { int T; scanf("%d", &T); while(T--) { scanf("%d%d", &n, &m); Kosaraju(); int ans = 0; if(tmp != cnt) printf("No\n"); else printf("Yes\n"); } return 0; }
tarjan代码
#include <iostream> #include <map> #include <cstdio> #include <stack> #include <cstring> #include <algorithm> #define MAXN 10005 #define MAXM 100005 #define INF 1000000000 using namespace std; int n, m; int scc;//强连通分量 int index;//每个节点的dfs访问次序编号 int dfn[MAXN];//标记结点i的dfs访问次序 int low[MAXN];//记录节点u或u的子树中的所有节点的最小标号 int fa[MAXN];//属于哪个分支 bool instack[MAXN];//是否在栈中 int in[MAXN], head[MAXN], e; int out[MAXN];//出度 stack <int>s; int tmp; int vis[MAXN]; struct Edge { int v, next; }edge[MAXM]; void insert(int x, int y) { edge[e].v = y; edge[e].next = head[x]; head[x] = e++; } void tarjan(int u) { dfn[u] = low[u] = ++index; s.push(u); instack[u] = true; for (int j = head[u]; j != -1; j = edge[j].next) { int v = edge[j].v; if(dfn[v] == 0)//未曾访问过 { tarjan(v); low[u] = min(low[u], low[v]); } else if(instack[v]) low[u] = min(low[u], dfn[v]); } if(dfn[u] == low[u]) { scc++; while(1) { int tmp = s.top(); s.pop(); instack[tmp] = 0; fa[tmp] = scc; if(tmp == u) break; } } } void init() { scc = index = 0; memset(dfn, 0, sizeof(dfn)); memset(instack, 0, sizeof(instack)); e = 0; memset(head, -1, sizeof(head)); memset(in, 0, sizeof(in)); memset(out, 0, sizeof(out)); } void dfs(int v, int deep) { tmp = max(tmp, deep); vis[v] = 1; for(int i = head[v]; i != -1; i = edge[i].next) if(!vis[edge[i].v]) { if(fa[edge[i].v] == fa[v]) dfs(edge[i].v, deep); else dfs(edge[i].v, deep + 1); } } void solve() { for (int i = 1;i <= n; i++) { if (!dfn[i]) tarjan(i); } for (int i = 1;i <= n; i++) { for(int j = head[i]; j != -1; j = edge[j].next) { int u = fa[i]; int v = fa[edge[j].v]; if(u != v) { out[u]++; in[v]++; } } } tmp = 0; memset(vis, 0, sizeof(vis)); for(int i = 1; i <= n; i++) if(in[fa[i]] == 0) { dfs(i, 1); break; } printf(tmp == scc ? "Yes\n" : "No\n"); } int main() { int T, x, y; scanf("%d", &T); while(T--) { init(); scanf("%d%d", &n, &m); while(m--) { scanf("%d%d", &x, &y); insert(x, y); } solve(); } return 0; }