UOJ #67 新年的毒瘤 (发现性质与Tarjan找割点)
题目描述:
有一幅$n$个点$m$条边的图,问单独删掉哪些点,可以使原图变成一棵树。(保证至少有一个解)
解题思路:
注意到树的性质是有$n$个点,$n-1$条边的联通图。而删掉一个点后新图有$n-1$个点,所以需要选择的点的度数是$m-(n-2)$。而要保证新图联通,所以要先$Tarjan$找割点,不要删割点就可以了。
代码:
1 #include <cstdio> 2 #include <cstring> 3 #include <algorithm> 4 using namespace std; 5 6 const int N = 1e5 + 10; 7 int n, m, tot, r[N], deg[N], sum, cut[N], low[N], dfn[N], tim; 8 9 struct edge {int t, n;} e[N * 2]; 10 11 void add(int x, int y) { 12 e[++ tot].t = y; e[tot].n = r[x]; r[x] = tot; 13 } 14 15 void dfs(int u, int per) { 16 int chi = 0; 17 dfn[u] = low[u] = ++ tim; 18 for (int i = r[u]; i; i = e[i].n) { 19 int v = e[i].t; 20 if (!dfn[v]) { 21 chi ++; 22 dfs(v, u); 23 low[u] = min(low[u], low[v]); 24 if (low[v] >= dfn[u]) cut[u] = 1; 25 } else if (v != per) { 26 low[u] = min(low[u], low[v]); 27 } 28 } 29 if (u == 1 && chi == 1) cut[u] = 0; 30 } 31 32 int main() { 33 scanf("%d %d", &n, &m); 34 for (int i = 1; i <= m; i ++) { 35 int u, v; 36 scanf("%d %d", &u, &v); 37 deg[u] ++, deg[v] ++; 38 add(u, v), add(v, u); 39 } 40 if (m == n - 1) { 41 for (int i = 1; i <= n; i ++) if (deg[i] == 1) sum ++; 42 printf("%d\n", sum); 43 for (int i = 1; i <= n; i ++) if (deg[i] == 1) printf("%d ", i); 44 } else if (m == n - 2) { 45 puts("1"); 46 for (int i = 1; i <= n; i ++) if (!deg[i]) printf("%d", i); 47 } else { 48 dfs(1, 0); 49 for (int i = 1; i <= n; i ++) if (!cut[i] && deg[i] == m - n + 2) sum ++; 50 printf("%d\n", sum); 51 for (int i = 1; i <= n; i ++) if (!cut[i] && deg[i] == m - n + 2) printf("%d ", i); 52 } 53 return 0; 54 }