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 }

 

posted @ 2016-08-11 22:46  Awner  阅读(137)  评论(0编辑  收藏  举报