luogu3225 矿场搭建 题解

本题思路还是比较清晰的。

显然的,求出 v-DCC 并缩点,然后判断方案数。

在本题中,可以只用 Tarjan 算法求出割点。标记割点,从其他的点依次 DFS,统计每颗搜索树上割点的数量(不重复统计)。

若图中没有割点,那么图中任选两个节点都能满足条件,答案 $ (2,C_n^2)$

若搜索树上割点为 1,则由于树上两点之间有且仅有一条简单路径,所以这颗搜索树中一定至少选一个点,累加答案,累乘方案数。

若割点多于 1,那么对答案是没有贡献的。

因为无论哪一个点被破坏,该搜索树都会分裂成为上述两种情况。

这种做法实现细节较多。

姑且算是 $ O(n^2)$?

#include<cstdio>
#include<cstring>
#include<iostream>
using namespace std;
#define R register
#define SET(x,y) memset(x,y,sizeof(x))
const int N=505;
int t, n, m, num, cnt, fg, ans1, dfn[N], low[N], v[N], w[N];
int c, h[N], ver[N<<1], nxt[N<<1];
long long ans2;
void add(int x,int y) { ver[++c]=y, nxt[c]=h[x], h[x]=c; }
void tarjan(int x) {
    R int i, y;
    dfn[x]=low[x]=++num, v[x]=1;
    for(i=h[x];i;i=nxt[i]) {
        y=ver[i];
        // v数组在一定程度上起到了dfn数组的作用,可以少清空一个数组
        if(!v[y]) {
            tarjan(y);
            low[x]=min(low[x],low[y]);
            if(dfn[x]<=low[y]) ++v[x];
        } else low[x]=min(low[x],dfn[y]);
    }
    if((x==1&&v[x]>2)||(x>1&&v[x]>1)) v[x]=2;
    // 1为普通点,2为割点。注意v[x]在上面已经初始化为1
}
void dfs(int x,int z) {
    R int i, y;
    v[x]=114514, ++cnt;
    // 放置重复搜索,统计节点数
    for(i=h[x];i;i=nxt[i]) {
        y=ver[i];
        if(v[y]==1) dfs(y,z);
        else if(v[y]==2&&w[y]!=z) ++fg, w[y]=z;
        // 防止搜索成环
    }
}
void sol() {
    SET(v,0), SET(w,0), SET(h,0);
    c=ans1=n=0, ans2=1ll;
    R int i, x, y;
    for(i=1;i<=m;++i) {
        scanf("%d%d",&x,&y);
        add(x,y), add(y,x);
        n=max(n,max(x,y));
    }
    tarjan(1);
    // luogu给出的数据是联通的
    for(i=1,num=0;i<=n;++i) if(v[i]==1) {
        fg=cnt=0, ++num;
        dfs(i,num);
        if(fg==1)  ++ans1, ans2*=cnt;
        // 从非割点搜索
    }
    if(!ans1) ans1=2, ans2=n*(n-1)/2;
    printf("Case %d: %d %lld\n",++t,ans1,ans2);
}
int main() { while(scanf("%d",&m)&&m) sol(); }

luogu3225 矿场搭建 题解
https://yozora0908.github.io/2021/lg3225-solution/
作者
yozora0908
发布于
2021年10月16日
许可协议