bzoj2730/UVALIVE5135 矿场搭建 求割点

前(sai)记(hua)

第一次见到这道题是在某大佬的博客里(那时它还叫UVALIVE5135),以为它是点双连通的裸题(然而我最后没有用点双),没调出来,未果。
在班上大佬讲课时又遇见它【然后它变成了bzoj的题?】了,调出来了

解法

在一个连通图有多个连通分量时

该连通图矿井个数为割点为1的连通图的个数。
方案数为割点为1的连通图的点数-1(割点)相乘

在一个连通图只有一个连通分量时

该连通图的矿井个数为2
方案数为c(2,总点数)

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=5e4+5,M=5e4+5;
bool iscut[N],vis[N];
int low[N],dfn[N],idc;
int head[N],nxt[M],to[M],etot;
int u[N],v[N],c[N],cut[N],tot; 
void adde(int u,int v)
{
    to[++etot]=v;
    nxt[etot]=head[u];
    head[u]=etot;
}
void tarjan(int u,int fa)
{
    int son=0;
    low[u]=dfn[u]=++idc;
    for(int i=head[u];i;i=nxt[i]){
        int v=to[i];
        if(v==fa) continue;
        if(!dfn[v]){
            son++;
            tarjan(v,u);
            low[u]=min(low[v],low[u]);
            if(low[v]>=dfn[u]) iscut[u]=1;
        }   
        else low[u]=min(low[u],dfn[v]); 
    }
    if(u==fa&&son<=1) iscut[u]=0;  
}
int cnt;
int dfs(int u,int sta)
{
    int ans=0;
    vis[u]=1;cnt++;
    for(int i=head[u];i;i=nxt[i]){
        int v=to[i];
        if(vis[v]) continue;
        if(iscut[v]){
            if(cut[v]!=sta) {cut[v]=sta;ans++;}
            continue;
        }
        ans+=dfs(v,sta);
    }
    return ans;
}
void init()
{
    etot=0;idc=0;tot=0;
    memset(vis,0,sizeof(vis));
    memset(head,0,sizeof(head));
    memset(low,0,sizeof(low));
    memset(dfn,0,sizeof(dfn));
    memset(iscut,0,sizeof(iscut));
    memset(cut,0,sizeof(cut));
}
int main()
{
    int n,k=0;
    while(scanf("%d",&n)&&n){
        k++;
        init();
        for(int i=1;i<=n;i++){
            scanf("%d%d",&u[i],&v[i]);
            c[++tot]=u[i],c[++tot]=v[i];
        }
        sort(c+1,c+1+n*2);
        int sum=unique(c+1,c+1+n*2)-c-1;
        for(int i=1;i<=n;i++){
            u[i]=lower_bound(c+1,c+1+sum,u[i])-c;
            v[i]=lower_bound(c+1,c+1+sum,v[i])-c;
            adde(u[i],v[i]);
            adde(v[i],u[i]);
        }
        tarjan(1,1);
        int exit=0;long long ans=1;
        for(int i=1;i<=sum;i++)
        if(!vis[i]&&!iscut[i]){
            cnt=0;
            int x=dfs(i,i);
            if(x==1) {
                exit++;
                ans=(long long)ans*cnt;
            }
            if(!x){
                exit+=2;
                ans=(long long)ans*cnt*(cnt-1)/2; 
            }  
        }
        printf("Case %d: %d %lld\n",k,exit,ans);    
        //bzoj要改成lld? 
    }
    return 0;
} 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值