1. 程式人生 > 實用技巧 >割點模板P3388

割點模板P3388




1
package pro.proclass; 2 3 import java.io.BufferedReader; 4 import java.io.InputStreamReader; 5 import java.util.Arrays; 6 import java.util.StringTokenizer; 7 8 // 割點模板題目 9 public class P3388 { 10 // 鏈式向前星邊的例項 11 static class Edge { 12 int to; 13 int next;
14 15 public Edge(int to, int next) { 16 this.to = to; 17 this.next = next; 18 } 19 } 20 21 // dfn 儲存無向圖的訪問順序的編號 22 // low[u]表示頂點u及其子樹中的點,通過非父子邊(回邊),能夠回溯到的最早的點(dfn最小)的dfn值(但不能通過連線u與其父節點的邊) 23 // cnt[u]用來記錄u點是割點 24 // head 鏈式向前星的資料結構陣列 25
private static int[] dfn, low, cnt, head; 26 27 // root 全域性根節點,用來判斷根節點是不是割點 28 // num 訪問順序編號全域性變數 29 // n 圖中點的個數 30 // m 圖中邊的個數 31 // numb 鏈式向前星邊的編號 32 private static int root, num, n, m, numb; 33 private static Edge[] edges; 34 private static int maxn = 100010, maxm = 500010;
35 36 // 新增邊 37 private static void add(int u, int v) { 38 edges[numb] = new Edge(v, head[u]); 39 head[u] = numb++; 40 } 41 42 public static void main(String[] args) throws Exception { 43 BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); 44 StringTokenizer st = new StringTokenizer(br.readLine()); 45 n = Integer.parseInt(st.nextToken()); 46 m = Integer.parseInt(st.nextToken()); 47 dfn = new int[maxn]; // 訪問順序儲存陣列 48 low = new int[maxn]; // 訪問父節點 49 cnt = new int[maxn]; 50 edges = new Edge[2*maxm]; 51 head = new int[maxn]; 52 Arrays.fill(head, -1); 53 numb = 0; 54 55 for (int i = 0; i < m; i++) { 56 st = new StringTokenizer(br.readLine()); 57 int x = Integer.parseInt(st.nextToken()); 58 int y = Integer.parseInt(st.nextToken()); 59 add(x, y); 60 add(y, x); 61 } 62 63 num = 0; 64 for (int i = 1; i <= n; i++) { 65 if(dfn[i] == 0) { // 如果當前點沒有被訪問就開始尋找割點 66 root = i; // 起始點為根節點 67 targan(i); 68 } 69 } 70 71 int ans = 0; 72 for (int i = 1; i <= n; i++) { 73 if(cnt[i] == 1) ans++; 74 } 75 76 StringBuilder sb = new StringBuilder(); 77 for (int i = 1; i <= n; i++) { 78 if(cnt[i] == 1) sb.append(i+" "); 79 } 80 System.out.println(ans); 81 System.out.println(sb.toString()); 82 } 83 84 private static void targan(int x) { 85 dfn[x] = low[x] = ++num; // 訪問順序初始化時 dfn和low一樣 86 int count = 0; // 計算跟節點的分支 87 88 // 鏈式向前星遍歷邊的編號 89 for (int n = head[x]; n != -1 ; n = edges[n].next) { 90 int i = edges[n].to; 91 92 if(dfn[i] == 0) { // 沒有被訪問 93 count++; // 根節點分支加一 94 targan(i); // 深度搜索 95 // 找到最底層回溯進行更新當前x點的low值,當前點low和回溯子節點low取小 96 low[x] = Math.min(low[x], low[i]); 97 // 如果x是根節點,並且 根節點的分支大於1 說明根節點是割點,進行標記 98 // 如果x不是根節點,但是x的dfn小於子節點的 low值, 說明走向子節點i必須經過父節點x,所以父節點是割點 99 if((x == root && count > 1) || (x != root && dfn[x] <= low[i])) { 100 cnt[x] = 1; 101 } 102 } 103 // 如果找到最底部已經被訪問過,更新當前點的low通過回溯邊的 dfn比較 104 // 如果是最後一個節點,因為是無向邊會更新父節點的dfn為自己low, 所以判斷<= 如果求割邊,這裡需要排除父節點 並且判斷改為《 105 low[x] = Math.min(low[x], dfn[i]); 106 } 107 } 108 }

題目背景

割點

題目描述

給出一個nn個點,mm條邊的無向圖,求圖的割點。

輸入格式

第一行輸入兩個正整數n,mn,m。

下面mm行每行輸入兩個正整數x,yx,y表示xx到yy有一條邊。

輸出格式

第一行輸出割點個數。

第二行按照節點編號從小到大輸出節點,用空格隔開。

輸入輸出樣例

輸入 #1
6 7
1 2
1 3
1 4
2 5
3 5
4 5
5 6
輸出 #1
1 
5

說明/提示

對於全部資料,1\leq n \le 2\times 10^41n2×104,1\leq m \le 10^51m105。

點的編號均大於00小於等於nn。

tarjan圖不一定聯通。