BZOJ 2730矿场搭建:割点

题意:

给出一个无向完全图,先在需要在图中选出最少的点,使得满足:删掉任意一个点之后,剩下的点都可以到达至少一个选择的点。求出最少选择的点数,以及选点的方案数。

题解:

显然,本题的描述本质上就是双连通性,即所有点到指定点都是点双连通的。那么考虑原图的割点,如果一个点双只包括一个割点,那么如果删掉了割点,就使得这个点双与原图断开连接,因此这个点双内一定要选出一个点(不能是割点);如果一个点双包括两个以上的割点,那么删掉某一个割点,可以向另外一个割点的方向搜寻关键点,已知搜寻到上面那种点双内部就可以找到。因此,只需要在每个只包含一个割点的点双内的非割点中选择一个点,就是最小方案。
一种比较好的处理方法是:先找到割点,然后对每一个非割点,dfs出块的大小,和包含割点个数。
另外。。。方案数会爆int。。。LL即可
Code:

#include<bits/stdc++.h>
//#define local
using namespace std;
const int maxn = 1500+100;
const int maxm = 1e3+100;
int T;
int m,n;
int first[maxn],des[maxm*2],nxt[maxm*2],tot;
int dfn[maxn],low[maxn],dfs_clock;
bool isCut[maxn];
int vis[maxn];
int sz,cutCnt,cutTot;
void init(){
    memset(first,0,sizeof first);
    tot=0;
    memset(dfn,0,sizeof dfn);
    memset(low,0,sizeof low);
    dfs_clock =0;
    memset(isCut,0,sizeof isCut);
    memset(vis,0,sizeof vis);
    n=0;
    cutTot=0;
}
inline void addEdge(int x,int y){
    tot ++;
    des[tot] =y;
    nxt[tot] = first[x];
    first[x] = tot;
}
void input(){
    while (m--){
        int u,v;
        cin>>u>>v;
        n = max(n,max(u,v));
        addEdge(u,v);
        addEdge(v,u);
    }
}
void tarjan(int x,int fa){
    dfn[x] = low[x] = ++dfs_clock;
    int child =0;
    for (int t = first[x] ;t;t=nxt[t]){
        int v = des[t];
        if (v==fa)continue;
        if (!dfn[v]){
            tarjan(v,x);
            child++;
            low[x] = min(low[x],low[v]);
            if (low[v]>=dfn[x]&&fa!=-1||fa==-1&&child>1){
                isCut[x] = 1;
            }
        }else if (dfn[v]<dfn[x]){
            low[x] = min(low[x],dfn[v]);
        }
    }
}
void dfs(int x,int flag){
    sz++;
    vis[x] = flag;
    for (int t = first[x];t;t=nxt[t]){
        int v = des[t];
        if (isCut[v]){
            if (vis[v]!=flag)cutCnt++,vis[v] = flag;
        }else if (vis[v]!=flag){
            dfs(v,flag);
        }
    }
}
void work(){
    cout<<"Case "<<T<<": ";
    for (int i=1;i<=n;i++){
        if (!dfn[i]){
            tarjan(i,-1);
        }
    }
    for (int i=1;i<=n;i++){
        cutTot+=isCut[i];
    }
    if (cutTot==0){
        cout<<2<<" "<<n*(n-1)/2<<endl;
        return ;
    }
    long long ans=1;
    int ans2=0;
    for (int i=1;i<=n;i++){
        if (!vis[i]&&!isCut[i]){
            sz=cutCnt=0;
            dfs(i,i);
            if (cutCnt==1)ans = ans*sz,ans2++;
        }
    }
    cout<<ans2<<" "<<ans<<endl;
}
void solve(){
    init();
    input();
    work();
}
int main(){
    #ifdef local
    freopen("input.in","r",stdin);
    #endif // local
    for (T=1,cin>>m;m;T++,cin>>m){
        solve();
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值