蒟蒻也是才开始看双联通分量,如果有不妥的地方还请巨佬们指出
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 }