[UVA][SCC變形] 10510 - Cactus
Cactus
Input: standard input
Output: standard output
![](http://uva.onlinejudge.org/external/105/p10510a.jpg)
A directed graph is a set V of vertices and a set of E ∈ {V x V} edges. An edge (u,v) is said to be directed from u to v (the edge (v,u) has the opposite direction). A directed cycle in a directed graph is a sequence of edges
(u1, v1), (u2, v2),…, (uk, vk)
such that ui+1 = vi for i = 1, …, k-1, and u1=vk. The directed cycle is simple if ui ≠ uj whenever i ≠ j (i.e., if it does not pass through a vertex twice).
In a strongly connected directed graph, there is for every pair u,v of vertices some directed cycle (not necessarily simple) that visits both u and v.
![](http://uva.onlinejudge.org/external/105/p10510b.jpg)
A directed graph is a cactus if and only if it is strongly connected and each edge is part of exactly one directed simple cycle. The first graph is a cactus, but the second one is not since for instance the edge (0,1) is in two simple cycles.
The reason for the name is that a "cactus" consists of several simple cycles connected to each other in a tree-like fashion, making it look somewhat like a cactus.
Problem
Write a program that given a directed graph determines if it is a cactus or not. The graph can have several thousand vertices.
Input
The first line contains an integer which is the number of test cases (less than 20). Each test case starts a line with an integer n ≥ 0 followed by line with an integer m ≥ 0 giving the number of vertices (n) and edges (m) in a graph (at most 10,000 of each). The vertices are numbered 0 through n-1. The following m lines describe the edges as pairs of numbers u v denoting an edge directed from u to v. There will never be more than one edge from u to v for any pair of vertices u and v. There are no loops, i.e., no edges from a vertex to itself.Output
For each test case output a single line with a single string. Output "YES" if the graph is a cactus, and output "NO" if it is not.
Sample Input
2 4 5 0 1 1 2 2 0 2 3 3 2 4 5 0 1 1 2 2 3 3 0 1 3
Sample Output
YES NO
Problem setter: Mikael Goldmann
題目描述:
判斷這張圖是否會符合,恰好是一個 SCC 元件,以及每一條邊都只出現過一次於一個環上。
題目解法:
一樣使用 SCC 的遞迴樹,對於 back edge 時,將整個環標記。
因此不同於一般 SCC 寫法,需要多紀錄 parent。
而有些邊可能會使用多次,由於只看 back edge,多餘向下也肯定是多餘的邊。
#include <stdio.h>
#include <string.h>
#include <vector>
#include <queue>
#include <deque>
#include <algorithm>
using namespace std;
struct edge {
int to, cnt;
edge(int a=0, int b=0):
to(a) , cnt(b) {}
};
vector<edge> g[10005];
int vfind[10005], findIdx;
int stk[10005], stkIdx;
int in_stk[10005], visited[10005];
//
int parent[10005];
edge *parentUsed[10005];
int checkflag;
int scc(int nd) {
in_stk[nd] = visited[nd] = 1;
stk[++stkIdx] = nd;
vfind[nd] = ++findIdx;
int mn = vfind[nd], i;
for(i = 0; i < g[nd].size(); i++) {
if(!visited[g[nd][i].to]) {
parent[g[nd][i].to] = nd;//this problem process
parentUsed[nd] = &g[nd][i];//this problem process
mn = min(mn, scc(g[nd][i].to));
}
if(in_stk[g[nd][i].to]) {
mn = min(mn, vfind[g[nd][i].to]);
if(vfind[g[nd][i].to] <= vfind[nd]) {//this problem process
// cycly find.
if(checkflag) continue;
g[nd][i].cnt++;
if(g[nd][i].cnt > 1) checkflag = 1;
int next = nd;
while(checkflag == 0) {
parentUsed[parent[next]]->cnt++;
if(parentUsed[parent[next]]->cnt > 1) checkflag = 1;
next = parent[next];
if(next == g[nd][i].to)
break;
}
}
}
}
if(mn == vfind[nd]) {
do {
in_stk[stk[stkIdx]] = 0;
} while(stk[stkIdx--] != nd);
}
return mn;
}
int main() {
int testcase;
int n, m;
int i, j, k, x, y;
scanf("%d", &testcase);
while(testcase--) {
scanf("%d %d", &n, &m);
for(i = 0; i < n; i++)
g[i].clear();
for(i = 0; i < m; i++) {
scanf("%d %d", &x, &y);
g[x].push_back(edge(y, 0));
}
memset(visited, 0, sizeof(visited));
memset(in_stk, 0, sizeof(in_stk));
int component = 0;
checkflag = 0;
for(i = 0; i < n; i++) {
if(visited[i] == 0) {
findIdx = stkIdx = 0;
if(++component > 1) break;
scc(i);
}
}
for(i = 0; i < n; i++)
for(j = 0; j < g[i].size(); j++)
if(g[i][j].cnt != 1)
checkflag = 1;
if(component > 1) checkflag = 1;
puts(checkflag == 0 ? "YES" : "NO");
#define debug 0
#if debug == 1
for(i = 0; i < n; i++)
for(j = 0; j < g[i].size(); j++)
printf("%d -> %d %d\n", i, g[i][j].to, g[i][j].cnt);
printf("%d %d\n", checkflag, component);
#endif
}
return 0;
}