#89 E
problem link http://codeforces.com/contest/118/problem/E
17:31
The problem is easy to understand, try to transfering the undirected graph to the directed one, and mainting the strong connectivity.
17:41
One clue, how to judge whether a directed graph has strong connectivity, from any vertex, there is at least one directed path to any other vertex.
Ok, choosing a ramdom vertex, and do out-DFS, and then do in-DFS. If both in and out DFS can travel all other vertex, then a graph has strong connectivity.
17:45
Now consider another point, how the form the directed graph from the original undirected one, where to start.
Let consider the property of a directed graph that has strong connectivity.
1. every vertex should has at least two arcs, one in, one out.
Only this property I found for now, but I think this property can lead me to the solution. Making every vertex with one out-arc and one in-arc.
An out-arc also is the in-arc, just for two adjacent vertexs.
18:04
No, the idea above is not precise, another idea is , if G', G'' is subgraph of G, and both of them have strong connectivity, if and only if there are at least a pair of in-out arc between G' and G'' , then the graph composed by G' and G'' has strong connectivity. This theory is good, but not enough, because it still need two graph with strong connectivity.
19:03
Another clue, breaking the undirected arc into two directed arcs.
19:07
Using DFS, start from a ramdom vertex V, follow the search path and finally reach V again , if not reach, then the graph can't be strong connectivity, when reach V again, then a ring is formed, and all vertext in ring become a subgraph with strong connectivity, then remove all arcs in this subgraph, start DFS from all vertex in this subgraph, if any search path reach a vertex in subgraph again, then vertex in this path can join the subgraph, then become a bigger subgraph with strong connectivity.
19:37
The last one method should work, now implement, max vertex amount is 10^5, adjacent matrix will be waste, bacause max arc amount is 3 * 10^5. Try using Forward star representation.
19:47
Forward Star is not fit, because the alg needs to remove arc frequently, and Forward Star is base on array, okay, adjacent list.
2011-10-12
17:57
When implement the alg, the biggest challenge is how to prevent adding reverse arc to the search path, the reverse arc in search path will definitely form fake ring. At first, I thought that when popping the arc a->b from stack(search path), adding new arc b->c to search path, check whether b->c is the reverse arc of a->b can reach the goal, the assumption I made for this thought is that, in the DFS process, b->c will never occur, because b has already visited, but I was wrong. In the forward search process, the assumption is right, but in the backward(回溯) process, although b is visited, DFS will still select an arc emanated from it, and that arc could be b->a !!!
The alg can always search the reverse arc from the search path, but this method is inefficient.
18:18
Found the solution, it's easy, because the situation occurs in backward process, so if the arc a->b do in the search path, then it just stand before the reverse arc b->a in the search path.
18:23
Oh no, TLE... T_T
18:50
To reduce running time, I need to get rid of dupplicate calculation, so the first step is to find duplicate calculation. When finding ring, if the search path reach an vertex that not belong to sub_graph, but had been visited before, current strategy is to ignore this "wrong path" ( because it dosen't lead to a ring ), in fact, all arcs in this path could be deleted, all vertex could add to the sub_graph after finding a ring. If ring is found, then this path would become part of the sub_graph, if not, the alg exit, deleted these arcs do no harm to the result.
19:37
The above idea needs the alg to identify forward and backward process, in the forward process, vertex A reaching a visited vertex B is good to delete the arc A-B, and continue search even when A has run out arcs, but in the backward process, if a vertex is running out arcs, means that the graph has no strong connectivity. But it seems difficult to indentify forward or backward.
21:05
No...my thought is completely wrong.
22:19
Finally, I implement a better alg, but still TLE. One regulation should write down is that, when an vertex A start DFS, if the search tree reaches A, then a partial ring is form, how to judge whether this ring is isolated from sub_graph ? If none of the ancestors of A are reached from that search tree, then the answer is yes, otherwise, it is no. When A is finishing its DFS, and prepare to backward search, the alg should check the mentioning condition.
2011-10-13
Accepted!, look back on the analysis I had made before, now I had the simple solution in my mind.
Maintain a subgraph the with strong connectivity, initialty, only one vertex in the subgraph.
Maintain the invariant below:
Start DFS from vertex in subgraph, record all deep level of any vertex in the search tree, also, if the search process reach to a vertex that not in subgraph, but had been reached before, then
record the minimun deep level of such vertexes.
If the search process reach to a vertex in subgraph, then add the search tree to subgraph, reset the minimun deep level traced before, and continue DFS ( by this time, the DFS either backward search or search to a vertex not in subgraph.)
If the DFS's backward search reach to a vertex not in subgraph, but has the current minimun deep level, then the whole graph will not have strong connectivity, because any search path in the search tree rooted from this vertex, cannot lead the way to subgraph or vertex visited before it.
#include <stdio.h>
#include <stdlib.h>
#include <memory.h>
#include <iostream>
using namespace std;
#define MAX_ARC 300001
#define MAX_NODE 100001
typedef struct arc_st {
int vertex, src;
struct arc_st *next;
struct arc_st *prev;
struct arc_st *reverse;
} arc_st, *arc_t;
arc_st pool[MAX_ARC * 2] = { 0 };
int pool_cnt = 0;
arc_t vertex_bucket[MAX_NODE] = { 0 };
arc_t new_arc() {
return &(pool[pool_cnt++]);
}
void add_arc(int v1, int v2) {
arc_t arc1 = new_arc();
arc_t arc2 = new_arc();
arc1->reverse = arc2;
arc2->reverse = arc1;
arc_t first_arc = vertex_bucket[v1];
arc1->vertex = v2;
arc1->src = v1;
if( NULL != first_arc ) {
arc1->next = first_arc;
first_arc->prev = arc1;
}
vertex_bucket[v1] = arc1;
first_arc = vertex_bucket[v2];
arc2->vertex = v1;
arc2->src = v2;
if( NULL != first_arc ) {
arc2->next = first_arc;
first_arc->prev = arc2;
}
vertex_bucket[v2] = arc2;
}
int output_counter = 0;
int output_arcs[2 * MAX_ARC] = { 0 };
void delete_arc(arc_t arc) {
// delete used arc, both direction
output_arcs[output_counter++] = arc->src;
output_arcs[output_counter++] = arc->vertex;
if( NULL != arc->next )
arc->next->prev = arc->prev;
if( NULL != arc->prev )
arc->prev->next = arc->next;
if( arc == vertex_bucket[arc->src] )
vertex_bucket[arc->src] = arc->next;
}
void delete_reverse_arc(arc_t arc) {
arc = arc->reverse;
if( NULL != arc->next )
arc->next->prev = arc->prev;
if( NULL != arc->prev )
arc->prev->next = arc->next;
if( arc == vertex_bucket[arc->src] )
vertex_bucket[arc->src] = arc->next;
}
bool is_visit[MAX_NODE] = { 0 };
bool is_sub_graph[MAX_NODE] = { 0 };
int tmp_set[MAX_NODE] = { 0 };
int tmp_set_size = 0;
int sub_graph_size = 0;
arc_t arc_stack[MAX_NODE] = { 0 };
int deep_lv[MAX_NODE] = { 0 };
int stack_cnt = 0;
int n, m;
bool find_ring(int start_v) {
memset(is_visit, 0, sizeof(int) * n);
memset(arc_stack, NULL, sizeof(int) * n);
memset(deep_lv, 0, sizeof(int) * n);
stack_cnt = 0;
int i, v, is_find_ring = false, min_deep = 0x7fffffff, tmp_size_size = 0;
arc_t arc = NULL;
arc_stack[stack_cnt++] = vertex_bucket[start_v];
deep_lv[start_v] = 0;
is_visit[start_v] = true;
while( stack_cnt != 0 ) {
arc = arc_stack[stack_cnt-1]; // pop the arc of this vertex
if( NULL != arc ) {
v = arc->vertex;
delete_reverse_arc(arc); // every arc even occurs in search path, its reverse arc will be deleted.
// find the vertex in sub graph with strong connectivity, good, vertex on path can join the sub graph
if( true == is_sub_graph[v] ) {
for( i=0;i<tmp_set_size;i++ ) {
if( false == is_sub_graph[tmp_set[i]] ) {
is_sub_graph[tmp_set[i]] = true;
sub_graph_size++;
}
}
tmp_set_size = 0;
min_deep = 0x7fffffff;
}
// find a vertex not visit, join it to path, and push stack
if( false == is_visit[v] ) {
is_visit[v] = true;
deep_lv[v] = deep_lv[arc->src] + 1;
// push next vertex's first arc on stack, in order to search deeper.
arc_stack[stack_cnt++] = vertex_bucket[v];
tmp_set[tmp_set_size++] = v; //add pending vertex to tmp_set
}
else { // hit a visited vertex, but not in sub_graph.
if( false == is_sub_graph[v] && min_deep > deep_lv[v] ) //update the min_deep vertex that reach in the ring searching process
min_deep = deep_lv[arc->vertex];
//backward search process
do {
stack_cnt--;
arc = arc_stack[stack_cnt];
v = arc->vertex;
// the backward searching vertex has already been reach by its child, mean a partial ring is form,
// by this precondition, if this vertex has searched all its arcs and no bigger partial ring contains it(check this by deep_lv),
// then this ring can't reach the sub_graph.
if( NULL == arc->next && false == is_sub_graph[arc->src] && deep_lv[arc->src] <= min_deep )
return false;
delete_arc(arc);
}while( stack_cnt > 0 && NULL == arc->next );
arc_stack[stack_cnt++] = arc->next;
}
}
else {
break;
}
}
if( sub_graph_size == n )
return true;
else
return false;
}
int main() {
scanf("%d %d", &n, &m);
int i, v1, v2;
for( i=0;i<m;i++ ) {
scanf("%d %d", &v1, &v2);
add_arc(v1, v2);
}
is_sub_graph[1] = true;
sub_graph_size++;
if( find_ring(1) == false ) {
printf("0\n");
return 0;
}
for( i=0;i<m;i++ )
printf("%d %d\n", output_arcs[2*i], output_arcs[2*i+1]);
return 0;
}