GRAPH_ - Tìm khớp và cầu (Cơ bản)

Tags: tarjan, tree, dfs, dp, graph

Problem

https://vn.spoj.com/problems/GRAPH_

https://oj.vnoi.info/problem/GRAPH_

Xét đơn đồ thị vô hướng G = (V, E) có n(1<=n<=10000) đỉnh và m(1<=m<=50000) cạnh. Người ta định nghĩa một đỉnh gọi là khớp nếu như xoá đỉnh đó sẽ làm tăng số thành phần liên thông của đồ thị. Tương tự như vậy, một cạnh được gọi là cầu nếu xoá cạnh đó sẽ làm tăng số thành phần liên thông của đồ thị.

Vấn đề đặt ra là cần phải đếm tất cả các khớp và cầu của đồ thị G.

Input

  • Dòng đầu: chứa hai số tự nhiên n,m.
  • M dòng sau mỗi dòng chứa một cặp số (u,v) (u<>v, 1<=u<=n, 1<=v

Output

Gồm một dòng duy nhất ghi hai số, số thứ nhất là số khớp, số thứ hai là số cầu của G

Example

Input
10 12  
1 10  
10 2  
10 3  
2 4  
4 5  
5 2  
3 6  
6 7  
7 3  
7 8  
8 9  
9 7  
  
Output   
4 3  

Tutorial

Đây là bài toán tìm cầu khớp cơ bản đầu tiên cần biết. Ý tưởng thuật toán như sau (nên xem thêm Tài liệu chuyên tin quyển 1): Bắt chước ý tưởng từ thuật toán Tarjan, gọi num[u] là số thứ tự đỉnh duyệt đến, low[u] là giá trị thứ tự nhỏ nhất mà trong cây DFS gốc u.

Tạo thêm mảng pa[u] là đỉnh cha của u trong khi duyệt, pa[u] = 0 khi u là chốt của cây DFS (đỉnh được duyệt đầu tiên).

Ta thực hiện định chiều DFS trong quá trình duyệt dfs tương tự thuật toán Tarjan. Bây giờ vấn đề đặt ra cạnh nào là cầu, đỉnh nào là khớp.

  • Với cầu: Giả sử với cạnh (u,v) với u là cha trực tiếp của v trong quá trình dfs (u = pa[v]). Nếu như từ v theo các cung đã định hướng, không có cách lên được u thì (u,v) là cầu của đồ thị low[v] >= num[v]

  • Với khớp: Xét hai trường hợp:

    • Khi u là chốt của một cây DFS: (pa[u] = 0) nếu nó có hai nhánh con trong cây DFS đã định hướng thì khi hủy u sẽ tăng số TPLT => u là khớp
    • Khi u không là chốt: (pa[u] > 0) Gọi v là đỉnh con của u thì nếu từ v không có cách đi theo cung định hướng lên được các đỉnh trên u thì u lúc này là khớp low[v] >= num[u]. (cách kiểm tra: duyệt v = 1->n, sau đó xét upa[v]).

Submission

GRAPH_.cpp