[HNOI2012]矿场搭建

LuoguP3225

蒟蒻也是才开始看双联通分量,如果有不妥的地方还请巨佬们指出

Code:

 1 #include <bits/stdc++.h>
 2 #define ll long long
 3 using namespace std;
 4 const int N = 1e4 + 7;
 5 int n, m;
 6 int tot, head[N];
 7 bool cut[N];//记录一个点是不是割点 
 8 vector<int> dcc[N];//记录每个点双里面点 
 9 int id, top, vdcc, dfn[N], low[N], stk[N];
10 struct edge{
11     int net, to;
12 }e[N * 3];
13 void add(int u, int v) {
14     e[++tot] = {head[u], v};
15     head[u] = tot;
16 }
17 void tarjan(int u, int fa) {//求点分模板 
18     dfn[u] = low[u] = ++id;
19     stk[++top] = u;
20     if (u == fa && !head[u]) {//单独的点就计入单独的点分 
21         dcc[++vdcc].push_back(u);
22         return;
23     }
24     int flag = 0;
25     for (int i = head[u]; i; i = e[i].net) {
26         int to = e[i].to;
27         if (!dfn[to]) {
28             tarjan(to, u);
29             low[u] = min(low[u], low[to]);
30             if (low[to] >= dfn[u]) {
31                 flag++;
32                 if (u != fa || flag > 1) {
33                     cut[u] = true;//记下割点 
34                 }
35                 vdcc++;
36                 dcc[vdcc].clear();//多组数据要清空 
37                 int y;
38                 do {
39                     y = stk[top--];
40                     dcc[vdcc].push_back(y);
41                 } while (y != to);//注意这里是to 
42                 dcc[vdcc].push_back(u);
43             }
44         } else {
45             low[u] = min(low[u], dfn[to]);
46         }
47     }
48 }
49 int main () {
50     int Case = 0;
51     while (scanf("%d", &m) && m != 0) {
52         Case++;
53         ll ans1 = 0, ans2 = 1; 
54         vdcc = tot = top = id = 0;
55         memset(head, 0, sizeof head);
56         memset(cut, 0, sizeof cut);
57         memset(dfn, 0, sizeof dfn);
58         //注意一下多组数据的清空,low数组会被id更新,不用清 
59         n = 0;
60         for(int i = 1; i <= m; i++) {
61             int u, v;
62             scanf("%d%d", &u, &v);
63             add(u, v), add(v, u);
64             n = max(n, max(u, v));//题里面没说点标号就只好自己取max 
65         }
66         for (int i = 1; i <= n; i++) {
67             if (!dfn[i]) {
68                 tarjan(i, i);
69             }
70         }
71         for (int i = 1; i <= vdcc; i++) {
72             int cutnum = 0;
73             int siz = (int)dcc[i].size();
74             for (int j = 0; j < siz; j++) {
75                 if (cut[dcc[i][j]]) {
76                     cutnum++;//记录该点分之中的割点数 
77                 }
78             }
79             if (siz == 1) {//单独的点特判一下 
80                 ans1++;
81                 continue;
82             }
83             if (cutnum == 1) {
84                 ans1++;
85                 ans2 *=(siz - 1);
86                 //如果只有一个割点的话,必须要再建一个非割点的出口
87                 //这样才能保证在割点坍塌之后该点分还能出去 
88             } else if (!cutnum) {
89                 ans1 += 2;
90                 ans2 *= siz * (siz - 1) / 2;
91                 //如果没有割点的话就必须建两个出口,然后就是C(n,2),化简后就是n*(n-1)/2
92             }
93             //还有一种情况就是有两个割点
94             //此时无论哪个点塌了,该点分里的点都能有地方出去,所以不需要建出口了 
95         }
96         printf("Case %d: %lld %lld
", Case, ans1,ans2);
97     }
98     return 0;
99 }
View Code
原文地址:https://www.cnblogs.com/Sundial/p/11830593.html