洛谷-P3225 [HNOI2012]矿场搭建

(有任何问题欢迎留言或私聊 && 欢迎交流讨论哦

目录

题意:传送门

 原题目描述在最下面。
 m条无向边链接两个矿洞。在某些矿洞设置安全出口,使得无论哪一个点毁坏,所有的工人都能逃到安全出口。求设置的安全出口的最小数量 ans1 a n s 1 ,以及数量方案 ans2 a n s 2 .

 超级无敌的类似这一题:传送门。那个题的毁坏一条边,本题是毁坏一个点。

思路:

垃圾题目毁我青春!
 找了一下午的bug,换了两种邻接表都没过。结果发现是 n n 没有置零,我去~

方法一:
 标记所有的割点。然后暴力dfs所有没有被访问过的不是割点的点,统计该点双联通分量内割点的数量 cut c u t ,和非割点的数量 cnt c n t 。如果 cnt=1 c n t = 1 ans1++ a n s 1 + + ans2=cnt a n s 2 ∗ = c n t ;如果 cnt=2 c n t = 2 ans1+=2,ans2=(cnt(cnt1)/2) a n s 1 + = 2 , a n s 2 ∗ = ( c n t ∗ ( c n t − 1 ) / 2 )

方法二:
 同样先求出所有的割点,然后再来一遍 tarjan t a r j a n ,点双联通分量缩点(该联通分量最后一个点不 pop p o p 出栈),统计该联通分量内割点的数量 cut c u t ,和非割点的数量 cnt c n t 。计算公式同上。


AC代码:
#include<bits/stdc++.h>
#define mme(a,b) memset((a),(b),sizeof((a)))
#define fuck(x) cout<<"* "<<x<<"\n"
#define all(x) (x).begin(),(x).end()
using namespace std;
typedef long long LL;
const int N = 1e4+5;
const int INF = 0x3f3f3f3f;
const int mod = 1e9 + 7;
int n, m;
struct lp{
  int v,nex;
}cw[N];
int dfn[N],low[N],inde,vis[N],qltNum,head[N],tot;
int stak[N*100],top;
int iscut[N],belong_num[N];
int cut,cnt,col,rt;
LL ans1,ans2;
void dfs1(int u,int Fa){
  dfn[u]=low[u]=++inde;
  int son=0;
  for(int i=head[u];~i;i=cw[i].nex){
    int v = cw[i].v;
    if(v==Fa)continue;
    if(!dfn[v]){
      dfs1(v,u);
      son++;
      low[u]=min(low[u],low[v]);
      if(low[v]>=dfn[u]){
        iscut[u]=1;
      }
    }else low[u]=min(low[u],dfn[v]);
  }
  if(son==1&&u==rt)iscut[u]=0;
}
void dfs2(int u,int Fa){
  vis[u]=col;
  cnt++;
  for(int i=head[u];~i;i=cw[i].nex){
    int v = cw[i].v;
    if(v==Fa)continue;
    if(vis[v]>=col)continue;
    if(iscut[v]){
      cut++;vis[v]=col;
    }else {
      dfs2(v,u);
    }
  }
}
void tarjan(){
  for(int i=1;i<=n;++i){
    if(!dfn[i]){
      rt=i;
      dfs1(i,-1);
    }
  }
  ans1=0;ans2=1;
  mme(vis,0);
  for(int i=1;i<=n;++i){
    cnt=cut=0;col++;
    if(vis[i]==0&&iscut[i]==0){
      dfs2(i,-1);
      if(cut==1){
        ans1++;
        ans2*=cnt;
      }else if(cut==0){
        if(cnt==1){
          ans1++;
        }else{
          ans1+=2;
          ans2=cnt*(cnt-1)/2*ans2;
        }
      }
    }
  }
}
inline void init(){
  mme(iscut,0);mme(belong_num,0);
  mme(dfn,0);mme(low,0);mme(vis,0);
  top=inde=qltNum=col=0;
  mme(head,-1);tot=-1;
}
inline void add(int u,int v){
  cw[++tot].v=v;cw[tot].nex=head[u];
  head[u]=tot;
  cw[++tot].v=u;cw[tot].nex=head[v];
  head[v]=tot;
}
int main(){
  int tc=0;
  while(~scanf("%d", &m)&&m){
    init();
    n=0;
    for(int i=0,u,v;i<m;++i){
      scanf("%d%d",&u,&v);
      n=max(n,max(u,v));
      add(u,v);
    }
    tarjan();
    printf("Case %d: %lld %lld\n", ++tc, ans1,ans2);
  }
  return 0;
}


原题目描述:

这里写图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值