[题解]P3225 [HNOI2012]矿场搭建

题意

给定一个无向图,问最少在几个点上设置出口,可以使得不管其他哪个点坍塌,其余所有点都可以与某个出口相连。

思路

对于这道题,我们有以下几个技巧:

  1. 对于每一个连通块出口数量必须大于等于 \(2\)
  2. 因为,如果出口数量为 \(1\),那么,如果刚好在出口坍塌了,就出不去了。
  3. 分别看每一个连通块。
  4. 这里要分三种情况来讨论:
    • 点双连通分量的度数为 \(0\)。(没有割点)。假设当前连通块的节点数量为 \(s\) 个,那么,方案数就为:\(C_s^2\)。即:\(\frac{s \times (s - 1)}{2}\)
    • 点双连通分量的度数为 \(1\)。则需要在连通分量内部设置一个出口。
    • 点双连通分量的度数大于 \(1\)。则不需要设置出口。

Code

#include <bits/stdc++.h>  
#define re register  
  
using namespace std;  
  
typedef unsigned long long ull;  
const int N = 510,M = 1e3 + 10;  
int n,m,idx,tim,num,opt,root;  
int h[N],ne[M],e[M];  
int dfn[N],low[N],vis[N];  
stack<int> s;  
vector<int> g[N];  
  
inline int read(){  
    int r = 0,w = 1;  
    char c = getchar();  
    while (c < '0' || c > '9'){  
        if (c == '-') w = -1;  
        c = getchar();  
    }  
    while (c >= '0' && c <= '9'){  
        r = (r << 3) + (r << 1) + (c ^ 48);  
        c = getchar();  
    }  
    return r * w;  
}  
  
inline void add(int a,int b){  
    ne[idx] = h[a];  
    e[idx] = b;  
    h[a] = idx++;  
}  
  
inline void tarjan(int u){//缩点   
    int cnt = 0;  
    dfn[u] = low[u] = ++tim;  
    s.push(u);  
    if (u == root && !~h[u]){  
        g[++num].push_back(u);  
        return;  
    }  
    for (re int i = h[u];~i;i = ne[i]){  
        int j = e[i];  
        if (!dfn[j]){  
            tarjan(j);  
            low[u] = min(low[u],low[j]);  
            if (dfn[u] <= low[j]){  
                cnt++;  
                if (u != root || cnt > 1) vis[u] = true;  
                int x;  
                num++;  
                do{  
                    x = s.top();  
                    s.pop();  
                    g[num].push_back(x);  
                }while (x != j);  
                g[num].push_back(u);  
            }  
        }  
        else low[u] = min(low[u],dfn[j]);  
    }  
}  
  
int main(){  
    while (m = read()){  
        int res = 0;//初始化   
        ull ans = 1;  
        for (re int i = 1;i <= n;i++) g[i].clear();  
        idx = tim = num = n = 0;  
        memset(h,-1,sizeof(h));  
        memset(dfn,0,sizeof(dfn));  
        memset(vis,false,sizeof(vis));  
        while (!s.empty()) s.pop();  
        for (re int i = 1;i <= m;i++){  
            int a,b;  
            a = read();  
            b = read();  
            n = max(n,max(a,b));  
            add(a,b);  
            add(b,a);  
        }  
        for (re int i = 1;i <= n;i++){  
            if (!dfn[i]){  
                root = i;  
                tarjan(i);  
            }  
        }  
        for (re int i = 1;i <= num;i++){  
            int cnt = 0;  
            int len = g[i].size();  
            for (auto u:g[i]){  
                if (vis[u]) cnt++;//统计度   
            }  
            if (!cnt){//分情况讨论   
                if (len > 1){  
                    res += 2;  
                    ans *= len * (len - 1) >> 1;  
                }  
                else res++;  
            }  
            else if (cnt == 1){  
                res++;  
                ans *= (len - 1);  
            }  
        }  
        printf("Case %d: %d %llu\n",++opt,res,ans);  
    }  
    return 0;  
}  
posted @ 2024-06-26 12:34  WBIKPS  阅读(46)  评论(0)    收藏  举报