[bzoj-2730][HNOI2012]矿场搭建 题解

7 篇文章 0 订阅
3 篇文章 0 订阅

题目传送门
题意解析:题目就是给了你一张无向图,让你设置任意的点为出口,然后去除任意一个点,其他点都能有一条路径到你设置的那些出口,求最少设置的出口数量。


My opinion:一开始看到这题,先随便画了几张图,然后发现了几个问题,这题可能产生环,我们都知道环上的两点之间都有两条及以上的简单路径,所以环上只需要放两个出口就好了(因为一个点没了,另一个点还在,如果只有一个出口的话,被去掉了就没了),不过这种图存在割点,不过如果一个环连着两个以上的割点,那这个环上就不需要放出口,因为如果一个割点没了,这个环上的点可以通过另外的割点到达别的出口。由这个可知,存在出口的连通块就是当每个割点都无法通过后,每个只连接一个割点的连通块。至于方案数用乘法原理,计算所有存在出口的连通块的点数之积。还有两种特殊情况,就是一个环和一条链的情况,相信如果看懂了上面所说,这两种情况很快就会想出来。(而且我已经说过一种了)
总结:
1、输入建图。
2、用tarjin求出每个割点。
3、用dfs跑每个连通块,求每个连通块的大小和连接的割点个数。
4、判断特殊情况后计算答案。


特别迷得代码:

#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<queue>
#define rep(i,a,n) for (int i=a;i<=n;i++)
#define per(i,a,n) for (int i=a;i>=n;i--)
#define Clear(a,x) memset(a,x,sizeof(a))
#define sqr(x) (x)*(x)
#define ll long long
#define db double
#define INF 200000000000000LL
#define eps 1e-8
using namespace std;
ll read(){
    ll x=0,f=1;
    char ch=getchar();
    while (ch<'0'||ch>'9') f=ch=='-'?-1:f,ch=getchar();
    while (ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
    return x*f;
}
const int maxn=1005;
int n,len,num,Time,m,top;
int vet[maxn<<1],Next[maxn<<1],head[maxn];
int dfn[maxn],low[maxn],q[maxn];
int belong[maxn];
ll ans;
void add(int u,int v){
    vet[++len]=v;
    Next[len]=head[u];
    head[u]=len;
}
void dfs(int u){
    dfn[u]=low[u]=++Time;
    for (int e=head[u];e;e=Next[e]){
        int v=vet[e];
        if (!dfn[v]){
            dfs(v);
            low[u]=min(low[u],low[v]);
            if (low[v]>=dfn[u]) belong[u]++;
        }else low[u]=min(low[u],dfn[v]);
    }
}
void find(int u){
    dfn[u]=low[u]=++Time;
    q[++top]=u;
    for (int e=head[u];e;e=Next[e]){
        int v=vet[e];
        if (dfn[v]) low[u]=min(low[u],dfn[v]);
            else {
                find(v);
                low[u]=min(low[u],low[v]);
                if (low[v]>=dfn[u]){
                    int t,temp=0,size=0;
                    do{
                        t=q[top--];
                        if (belong[t]>=2) temp++;
                        size++;
                    }while (q[top]!=u);
                    t=u;
                    if (belong[u]>=2) temp++;
                    size++;
                    if (!temp)
                        num+=2,ans*=size*(size-1)/2;
                    else if (temp==1)
                        num++,ans*=size-1;
                }
            }
    }
}
int main(){
    int Cas=0;
    while (~scanf("%d",&m)&&m!=0){
        Clear(head,0);
        Clear(dfn,0);
        Clear(belong,0);
        len=1;
        num=Time=top=0;
        rep(i,1,m){
            int u=read(),v=read();
            add(u,v);add(v,u);
            n=max(max(u,v),n);
        }
        rep(i,1,n)
            if (!dfn[i]) dfs(i);
                else belong[i]++;
        num=0;
        ans=1;
        Clear(dfn,0);
        rep(i,1,n)
            if (!dfn[i]) find(i);
        printf("Case %d: ",++Cas);
        printf("%d %lld\n",num,ans);
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值