[HNOI2012]礦場搭建node
這個題作的我十分自閉。。
沒看出這個是個點雙,而後一夜+半上午。。
一看確定和割點有關,咱們找到全部的點雙,會發現有這麼幾種狀況c++
須要注意的是
題目中沒有給出具體多少個點,但應該是連續的,由於我按連續的作的git
#include <bits/stdc++.h> #define int long long using namespace std; const int N = 1e6 + 10; int n, m, js, cnt, num, sum, size, tot, ans1, ans2; /* js計數器 sum聯通塊編號 tot聯通塊中割點個數 size聯通塊中點的個數 ans1第一問答案 ans2第二問答案 */ int head[N], dfn[N], low[N], bel[N]; bool cut[N], vis[N]; class node { public : int v, nx; } e[N]; template<class T>inline void read(T &x) { x = 0; int f = 0; char ch = getchar(); while (!isdigit(ch)) f |= (ch == '-'), ch = getchar(); while (isdigit(ch)) x = x * 10 + ch - '0', ch = getchar(); x = f ? -x : x; return; } inline void add(int u, int v) { e[++num].nx = head[u], e[num].v = v, head[u] = num; } void tarjan(int u, int fa) { dfn[u] = low[u] = ++cnt; int child = 0; for (int i = head[u]; ~i; i = e[i].nx) { int v = e[i].v; if (!dfn[v]) { tarjan(v, u); low[u] = min(low[u], low[v]); if (low[v] >= dfn[u] && u != fa) cut[u] = 1, vis[u] = 1; if (u == fa) child++; } low[u] = min(low[u], dfn[v]); } if (child >= 2 && u == fa) cut[u] = 1, vis[u] = 1; } void dfs(int u) { vis[u] = 1; size++; for (int i = head[u]; ~i; i = e[i].nx) { int v = e[i].v; if (!vis[v] && !cut[v]) dfs(v); //沒有被訪問過且不是割點 if (cut[v] && bel[v] != sum) bel[v] = sum, tot++; //用來標記割點在哪一個連通塊內 } } signed main() { while (scanf("%lld", &n) && n) { num = cnt = m = ans1 = 0; ans2 = 1; memset(e, 0, sizeof (e)); memset(dfn, 0, sizeof (dfn)); memset(low, 0, sizeof (low)); memset(cut, 0, sizeof (cut)); memset(vis, 0, sizeof (vis)); memset(bel, 0, sizeof (bel)); memset(head, -1, sizeof (head)); for (int i = 1, x, y; i <= n; ++i) { read(x), read(y); add(x, y), add(y, x); m = max(m, max(x, y)); } for (int i = 1; i <= m; ++i) if (!dfn[i]) tarjan(i, i); for (int i = 1; i <= m; ++i) { if (vis[i] || cut[i]) continue; tot = size = 0; sum++; dfs(i); if (tot == 0) ans1 += 2, ans2 *= ((size - 1) * size) >> 1; if (tot == 1) ans1++, ans2 *= size; } printf("Case %lld: %lld %lld\n", ++js, ans1, ans2); } }