BZOJ2730 矿场搭建

题目:http://www.lydsy.com/JudgeOnline/problem.php?id=2730

基本思路,注意到n非常的小,所以暴力即可。

首先显然我们要找出所有的割点,$O(n^2)$并查集。

我们将所有的割点删除,然后将剩余的边用并查集维护,化为很多个联通块。

有三种情况:

1.联通块和一个割点相连,这种情况下显然只能在当前联通块里任选一个点建一个逃生点。

2.与两个割点相连,这种情况下不用建逃脱点因为他只能割掉其中一个割点。

3.没有割点,至少建两个(任选两个点),因为有可能逃生点被破坏了,所以为两个。

至于情况数,因为同一联通块的点没有不同,所以直接乘起来。

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
#include <set>

#define N 510
#define p E[i].x
#define LL long long

using namespace std;

struct edge{
    int x,to;
}E[N<<1];

int m,n,g[N],totE,fa[N],cnt[N],siz[N];
set<int> G[N];
bool cut[N];

inline void addedge(int x,int y){
    E[++totE]=(edge){y,g[x]}; g[x]=totE;
    E[++totE]=(edge){x,g[y]}; g[y]=totE;
}

int find(int x){
    if(fa[x]!=x) fa[x]=find(fa[x]);
    return fa[x];
}
    
void add(int x,int y){
    int r1=find(x),r2=find(y);
    if(r1!=r2) fa[r2]=r1;
}

bool check(int x){
    for(int i=1;i<=n;i++) fa[i]=i;
    for(int i=2;i<=totE;i+=2)
        if(E[i].x!=x&&E[i^1].x!=x) add(E[i].x,E[i^1].x);
    int tmp=0;
    for(int i=1;i<=n;i++){
        if(i==x) continue;
        if(!tmp) tmp=find(i);
        if(find(i)!=tmp) return 1;
    }
    return 0;
}

int main(){

    int caset=0;
    while(scanf("%d",&m)==1){
        if(!m) break;
        for(int i=1;i<=n;i++){
            cut[i]=0;
            siz[i]=g[i]=cnt[i]=0;
            G[i].clear();
        }
        printf("Case %d: ",++caset);
        totE=1; n=0;
        for(int i=1,x,y;i<=m;i++){
            scanf("%d%d",&x,&y);
            addedge(x,y);
            n=max(n,x); n=max(n,y);
        }
        bool fl=0;
        for(int i=1;i<=n;i++)
            if(check(i)) cut[i]=1,fl=1;
        if(!fl){
            printf("%d %lld\n",2,((n-1)*(LL)n)/2);
            continue;
        }
        for(int i=1;i<=n;i++) fa[i]=i;
        for(int i=2;i<=totE;i+=2)
            if(!cut[E[i].x]&&!cut[E[i^1].x]) add(E[i].x,E[i^1].x);
        for(int x=1;x<=n;x++){
            siz[find(x)]++;
            if(!cut[x]) continue;
            for(int i=g[x];i;i=E[i].to)
                if(!cut[p]) G[find(p)].insert(x);
        }
        int ans=0;
        LL ansv=1;
        for(int i=1;i<=n;i++){
            if(cut[i]) continue;
            if(find(i)!=i) continue;
            if(G[i].size()<2){
                ans++;
                ansv*=siz[i];
            }
        }
        printf("%d %lld\n",ans,ansv);
    }
}
View Code

 

posted @ 2015-06-04 15:38  lawyer'  阅读(172)  评论(0编辑  收藏  举报