VOI 06 Bài 3 - Kênh xung yếu

Xem dạng PDF

Gửi bài giải

Điểm: 1,01 (OI)
Giới hạn thời gian: 0.9s
Giới hạn bộ nhớ: 512M
Input: stdin
Output: stdout

Nguồn bài:
Vietnam Olympiad of Informatics 2006 - Bảng B
Dạng bài
Ngôn ngữ cho phép
C, C++, Go, Java, Kotlin, Pascal, PyPy, Python, Rust, Scratch

Một hệ thống ~n~ máy tính (các máy tính được đánh số từ 1 đến ~n~) được nối lại thành một mạng bởi ~m~ kênh nối, mỗi kênh nối hai máy nào đó và cho phép truyền tin một chiều từ máy này đến máy kia. Ta gọi một mạch vòng của mạng đã cho là một dãy các máy tính và các kênh nối chúng có dạng:

~u_{1}~, ~e_{1}~, ~u_{2}~, ..., ~u_{i}~, ~e_{i}~, ~u_{i+1}~, ...,~u_{k-1}~, ~e_{k-1}~, ~u_{k}~, ~e_{k}~, ~u_{1}~

Trong đó ~u_{1}~, ~u_{2}~, ...,~u_{k}~ là các máy tính khác nhau trong mạng, ~e_{i}~ -- kênh truyền tin từ máy ~u_{i}~ đến máy ~u_{i+1}~ ~(i = 1, 2, ..., k-1)~, ~e_{k}~ là kênh truyền tin từ máy ~u_{k}~ đến máy ~u_{1}~ . Một kênh truyền tin trong mạng được gọi là kênh xung yếu nếu như bất cứ mạch vòng nào của mạng cũng đều chứa nó.

Yêu cầu: Hãy xác định tất cả các kênh xung yếu của mạng đã cho.

Input

Dòng đầu tiên chứa 2 số nguyên dương ~n~ và ~m~.

Dòng thứ i trong số m dòng tiếp theo mô tả kênh nối thứ i bao gồm hai số nguyên dương ~u­_ i~, ~v_i~ cho biết kênh nối thứ ~i~ cho phép truyền tin từ máy ~u_{i}~ đến máy ~v_{i}~ .

Các số trên cùng một dòng được ghi cách nhau bởi dấu cách.

Output

Dòng đầu tiên ghi số nguyên ~k~ là số lượng kênh xung yếu trong mạng đã cho. Ghi ~k = -1~ nếu mạng không chứa kênh xung yếu.

Nếu ~k > 0~ thì mỗi dòng trong số ~k~ dòng tiếp theo ghi thông tin về một kênh xung yếu tìm được theo qui cách mô tả giống như trong file dữ liệu vào. Đồng thời các kênh được in ra theo thứ tự từ điển

Giới hạn

Trong tất cả các test: ~n \leq 1000~, ~m \leq 20000~.

Có 50% số lượng test với ~n \leq 200~.

Sample Input

2 2
1 2
2 1

Sample Output

2
1 2
2 1

Bình luận

Hãy đọc nội quy trước khi bình luận.



  • 11
    LeQuangLiem  đã bình luận lúc 22, Tháng 8, 2025, 15:56 chỉnh sửa

    solve:

    tóm tắt đề:

    cho đồ thị có hướng n đỉnh m cạnh

    1 cạnh được gọi là xung yếu nếu mọi chu trình trong đồ thị đều chứa nó

    xác định các cạnh xung yếu

    1 test ví dụ khác rõ hơn:

    4 5

    1 2

    2 3

    3 1

    2 4

    4 1

    ở đây cạnh 1 2 là cạnh xung yếu và có 2 chu trình là 1 - 2 - 3 - 1 và 1 - 2 - 4 - 1 thì ta thấy cạnh 1 - 2 đều xuất hiện ở cả 2 chu tình => cạnh 1 - 2 là cạnh xung yếu


    phân tích:

    khi nhìn vào đề ta ngay lập tức thấy ngay 2 nhận xét.

    vì cạnh xung yếu đều phải nằm trong mọi chu trình của đồ thị.

    => nhận xét 1: nếu đồ thị có ít nhất 2 chu trình nằm ở ít nhất 2 vị trí khác nhau vì không thể nào có cạnh xung yếu.

    suy ra để cạnh xung yếu nằm trong mọi chu trình của đồ thị thì tất cả các chu trình phải nằm trong một scc (siêu đỉnh)

    nhận xét 2: dựa trên nhận xét 1, chỉ có duy nhất một siêu đỉnh được phép chứa chu trình. xét đồ thị con trong siêu đỉnh đó:

    ta có 2 trường hợp như sau:

    trường hợp 1: đồ thị là 1 vòng tròn (mỗi đỉnh có cạnh ra và cạnh vào = 1) thì đáp án sẽ là mọi cạnh trong đồ thị con đó

    trường hợp 2: đồ thị không phải là vòng tròn

    thì đối với cả 2 trường hợp, ra rút ra được 1 nhận xét: khi ta xóa 1 cạnh nào đó khiến cho đồ thị mất đi các chu trình và trở thành DAG thì đây chính là cạnh xung yếu bởi vì tất cả các chu trình đều phải chứa nó, hay nói cách khác giao của các chu trình đều là cạnh xung yếu đó và khi ta xóa nó đi thì đồ thị con này sẽ trở thành DAG => cạnh đó chính là cạnh xung yếu


    từ 2 nhận xét trên ta sẽ ra được các bước làm

    bước 1: tarjan nén đồ thị lại thành các siêu đỉnh

    bước 2: kiểm tra với mỗi siêu đỉnh, và đếm số lượng siêu đỉnh có chu trình lại nếu số lượng siêu đỉnh có chu trình != 1 thì không tồn tại cạnh xung yếu nào => -1

    bước 3: kiểm tra 2 trường hợp trong nhận xét 2 bước 3.1: trường hợp 1 thì sẽ in ra các cạnh trong đồ thị đó

    bước 3.2: trường hợp 2: ta phải dfs duyệt bỏ qua thử 1 cạnh, nếu đồ thị sau khi bỏ qua 1 cạnh mà vẫn còn chu trình thì trả về true, ngược lại false

    rồi xét mọi cạnh trong đồ thị con đó, nếu dfs(cạnh đang xét) = true tức là ta xóa nó xong đồ thị vẫn còn chu trình => trả về false, ngược lại nếu xóa nó xong mà đồ thị không còn là chu trình => đây là cạnh xung yếu -> trả về true

    lưu ý ta xét id cạnh để xóa chứ không xét trực tiếp cặp cạnh u - v, đồng thời loại bỏ cạnh trùng.


    code của mình:

    hehe

    chúc mọi người AC!


    • 0
      thkjconay  đã bình luận lúc 22, Tháng 8, 2025, 16:07

      hay qua ngai oi