[HNOI2012]礦場搭建 (點雙連通)

題目

[HNOI2012]礦場搭建node

解析

這個題作的我十分自閉。。
沒看出這個是個點雙,而後一夜+半上午。。
一看確定和割點有關,咱們找到全部的點雙,會發現有這麼幾種狀況c++

  1. 連通塊中一個割點也沒有,這時咱們至少要建兩個出口,以防萬一某個出口塌了就GG了,方案的話就從size(聯通塊大小)個點中隨便選兩個,也是\(\dbinom{size}{2}\)個。
  2. 聯通塊中有一個割點,若是這個割點塌了就GG了,須要一個出口,但若是塌的不是割點,咱們能夠經過割點跑到另外一個連通塊中,因此只須要割點。根據乘法原理,方案數只須要乘上這個連通塊的size。
  3. 聯通塊中大於兩個割點的時候,若一個割點塌了,能夠經過另外一個割點跑到另外一個聯通塊裏,因此不須要再加出口。

須要注意的是
題目中沒有給出具體多少個點,但應該是連續的,由於我按連續的作的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);
    }
}
相關文章
相關標籤/搜索