P3225 [HNOI2012]矿场搭建

目录

  • 题目链接
  • 图论的相关定义
  • 题解
  • 代码

好不错的一道题,花了大量知识ac了

题目链接

https://www.luogu.com.cn/problem/P3225

图论的相关定义

https://www.cnblogs.com/poi-bolg-poi/p/12183166.html
可直接在这看↓

题解

起初做的时候以为,寻找每个割点,在割点形成的联通块中判断。
每有一个联通块,ans1 ++; 联通块size相乘 得到ans2。
后来一个点都没过,思路是错误的。

正解 :

上边方法的错误在ans1, ans1错了 所以ans2也错了。
首先我们可以知道,求割点是必要的。
对于割点形成的点双,有以下几种情况

  • 不存在割点 rt
    P3225 [HNOI2012]矿场搭建_第1张图片
  • 一个割点 rt
    P3225 [HNOI2012]矿场搭建_第2张图片
    已标黑割点
  • 多个割点 rt
    P3225 [HNOI2012]矿场搭建_第3张图片

之所以分为这三种情况, 是因为所产生的答案计数不同。

开始分类讨论
$ 1° $ 不存在割点。

当出口矿点坍塌的时候,必然有需要另一个矿点可以作为出口.

每个点双彼此之间由割点连接构成,不存在割点即此点双完全不可到其他点双,需要单独考虑。

也就是在当前点双中任选两个点作为出口,$ C(n,2) $种可选情况.

$ 2 ° $ 一个割点。

当割点所在矿点坍塌时,必然会使图不连通

当该割点塌陷的时候,图就不会联通,必然要在当前点双里在建一个出口

如 样例1
P3225 [HNOI2012]矿场搭建_第4张图片

点双$ { $ $ 1,6,2,3,5 $ $ } $ $中,除去1点,皆符合情况。

$ 3° $ 多个割点。

因为存在多个割点,任意一个矿点坍塌都不会对图的连通性造成影响

当当前所在点双的某个割点塌陷后,图依然联通,必然可以走到其他的点双,从其他点双的出口出去,对答案无影响。
综上所述(雾) 。。。。

代码

挺重要的,还是要看看怎么写

#include
#define clear(a) memset(a, 0, sizeof a)
using namespace std;
const int N = 5e2+50;
struct node{ int next, to; }edge[N<<1];
int cnt, head[N];
inline void add(int from, int to) { edge[++cnt] = (node) {head[from], to}, head[from] = cnt;}
int low[N], dfn[N], visited[N], cut[N];
int tot, sky, size, num;

void tarjan(int u, int f) {
    low[u] = dfn[u] = ++tot; int child = 0;
    for(int i = head[u]; i; i = edge[i].next) {
        int v = edge[i].to;
        if(!dfn[v]) {
            tarjan(v, f), low[u] = min(low[u], low[v]);
            if(u == f) child ++;
            if(u != f && low[v] >= dfn[u]) cut[u] = 1;
        }else low[u] = min(low[u], dfn[v]);
    }
    if(u == f && child >= 2) cut[u] = 1;
}
void dfs(int u) {
    size++, visited[u] = sky;
    for(int i = head[u]; i; i = edge[i].next) {
        int v = edge[i].to;
        if(visited[v] != sky && cut[v]) num ++, visited[v] = sky;
        if(!visited[v] && !cut[v]) dfs(v);
    }
}

int main() {
    int n = 0, m, t = 0;
    while(1) {
        clear(head), clear(edge), clear(cut);
        clear(visited), clear(low), clear(dfn);
        t++;
        n = cnt = tot = 0;
        scanf("%d", &m);
        if(m == 0) break;
        for(int i = 1; i <= m; i++) {
            int a, b; scanf("%d%d", &a, &b);
            add(a, b), add(b, a);
            n = max(max(a, b), n);
        }
        for(int i = 1; i <= n; i++) if(!dfn[i]) tarjan(i, i);
        long long ans1 = 0, ans2 = 1;
        for(int i = 1; i <= n; i++) {
            if(!visited[i] && !cut[i]) {
                num = size = 0, sky++,dfs(i);
                if(num == 0) ans1 += 2, ans2 *= size* (size - 1) / 2;
                if(num == 1) ans1 ++, ans2 *= size;
            }
        }
        printf("Case %d: %lld %lld\n", t, ans1, ans2);
    }
    return 0;
}

你可能感兴趣的:(P3225 [HNOI2012]矿场搭建)