BZOJ2730-[HNOI2012]矿场搭建

本文详细解析了一道关于图论中割点与联通块的算法题目,通过删除割点形成联通块,并讨论了如何确定每个联通块是否需要设置出口,以及如何计算可能的方案数。文章提供了详细的代码实现,强调了细节处理的重要性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


题解:
一道水题交了5次才过……
只需考虑删去的点是割点的情况。
删去所有割点,形成若干联通块,假如图中只有一个联通块,则需设置两个出口。
若一个联通块可以到达两个割点,则该联通块内不必设置出口,否则必须设置出口。
方案数利用乘法原理计算即可。
一定要注意细节。
CodeCode:

#include<iostream>
#include<vector>
#include<algorithm>
#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<cstring>
#define N 100005
#define ll long long
using namespace std;
int low[N],dfn[N],deep,flag[N],head[N],tot,vis[N],b[N],a[N],cnt,sum,c[N];
struct node
{
    int vet,next;
}edge[N];
void add(int u,int v)
{
    edge[++tot].vet=v;
    edge[tot].next=head[u];
    head[u]=tot;
}
void tarjan(int u,int father)
{
    int son=0;
    low[u]=dfn[u]=++deep;
    for(int i=head[u];i!=-1;i=edge[i].next)
    {
        int v=edge[i].vet;
        if(!dfn[v])
        {
            son++;
            tarjan(v,u);
            low[u]=min(low[u],low[v]);
            if(low[v]>=dfn[u])flag[u]=true;
        }else if(v!=father)low[u]=min(low[u],dfn[v]);
    }
    if(father==-1&&son==1)flag[u]=false;
}
void dfs(int u)
{
    vis[u]=false;
    cnt++;
    for(int i=head[u];i!=-1;i=edge[i].next)
    {
        int v=edge[i].vet;
        if(vis[v])dfs(v);else
            if(flag[v]&&b[v])sum++,b[v]=false;
    }
}
int main()
{
    int m,tt=0;
    scanf("%d",&m);
    while(m!=0)
    {
        int n=0;tot=0;
        memset(head,-1,sizeof(head));
        for(int i=1;i<=m;i++)
        {
            int u,v;
            scanf("%d%d",&u,&v);
            add(u,v);add(v,u);
            n=max(n,u);n=max(n,v);
        }
        deep=0;
        memset(dfn,0,sizeof(dfn));
        memset(flag,false,sizeof(flag));
        for(int i=1;i<=n;i++)
            if(!dfn[i])tarjan(i,-1);
        memset(vis,true,sizeof(vis));
        int p=0;
        for(int i=1;i<=n;i++)
            if(flag[i])vis[i]=false;
        for(int i=1;i<=n;i++)
        {
            memset(b,true,sizeof(b));
            cnt=sum=0;
            if(vis[i])
            {
                dfs(i);
                a[++p]=cnt;
                c[p]=sum;
            }
        }
        int ans1;ll ans2;
        if(p==1)
        {
            ans1=2;
            ans2=(a[1]-1)*a[1]/2;
        }else
        {
            ans1=0,ans2=1;
            for(int i=1;i<=p;i++)
                if(c[i]==1)
                    ans1++,ans2*=a[i];
        }
        printf("Case %d: %d %lld\n",++tt,ans1,ans2);
        scanf("%d",&m);
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

JackflyDC

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值