#include <bits/stdc++.h>
using namespace std;

class Graph {
public:
	int n, m;
	bool isDirected;
	
    vector<vector<int>> adj;
    
    Graph(int nodes, bool isDigraph): adj(nodes){
        n = nodes;
        m = 0;
        isDirected = isDigraph;
    }
    
    void insertEdge(int u, int v){
    	if(u >=n || v >=n || u < 0 || v < 0){
    		cout<<"Either of the vertices doesn't exists!!\n"
    		"please enter vertices within range of [0,"<<n<<").\n";
    		return;
    	}
    	
    	if(isDirected || u != v)
    		adj[u].push_back(v);
        
        if(!isDirected && u != v)
        	adj[v].push_back(u);
    }
    
    void findCutEdgesAndVertices() {
    	
    	vector<bool> visited(n);
    
	    vector<bool> cutVertices(n);
	    vector<vector<int>> cutEdges;
	    
	    vector<int> discovery(n), low(n);
	    
	    int timer = 0;
        
        // Calling with root node of DFS with 0, parent -1 since it has no parent.
        // You can call with any node in the graph as root.
        dfsTraversal(0, -1, timer, visited, cutVertices, cutEdges, discovery, low);
        
        cout<<"The cut vertices(Articulation Points) in the graph are:,\n";
        for(int i=0; i<n; ++i){
            if(cutVertices[i])
                cout<<i<<" ";
        }
        cout<<"\n";
        
        cout<<"The cut edges(Bridges) in the graph are:,\n";
        for(auto edge: cutEdges){
                cout<<edge[0]<<" -> "<<edge[1]<<"\n";
        }
        cout<<"\n";
    }
    
    void dfsTraversal(int u, int parent, int &timer, vector<bool> &visited, 
    		vector<bool> &cutVertices, vector<vector<int>> &cutEdges, 
    		vector<int> &discovery, vector<int> &low){
    
        visited[u] = true;
        low[u] = discovery[u] = ++timer;
        
        int children = 0; //Useful for root node in DFS tree.
        
        for(auto v: adj[u]){
            if(!visited[v]){
                
                ++children;
                
                dfsTraversal(v, u, timer, visited, cutVertices, cutEdges, discovery, low);
                
                low[u] = min(low[u], low[v]);
                
                // If discovery of current node(u) is less than finish of any of its neighbour nodes,
                // then it means neighbour(v) has not found any route to another node which is discovered
                // before current node(u). Hence, a cut edge.
                if(discovery[u] < low[v])
                    cutEdges.push_back({u, v});
                
                // This is root node of dfs tree with more than 1 child, i.e cutVertex
                if(parent == -1 && children > 1)
                    cutVertices[u] = true;
                
                // This is starting point of cycle if found, i.e cutVertex
                else if(parent != -1 && discovery[u] <= low[v])
                    cutVertices[u] = true;
                
            }
            else if(v != parent){
                low[u] = min(low[u], low[v]);
            }
        }
    }
};


int main(){
	
	int n, di, m;
	cin>>n>>di>>m;
	
	Graph g(n, di);
	
	int u, v;
	
	for(int i = 0; i < m; ++i){
		cin>>u>>v;
		g.insertEdge(u, v);
	}
	
	g.findCutEdgesAndVertices();
	
	return 0;
}

