首页 > 代码库 > BZOJ 2730 矿场搭建(割点)

BZOJ 2730 矿场搭建(割点)

题目链接:http://61.187.179.132/JudgeOnline/problem.php?id=2730

题意:煤矿工地可以看成是由隧道连接挖煤点 组成的无向图。为安全起见,希望在工地发生事故时所有挖煤点的工人都能有一条出路逃到救援出口处。于是矿主决定在某些挖煤点设立救援出口,使得无论哪一个 挖煤点坍塌之后,其他挖煤点的工人都有一条道路通向救援出口。计算至少需要设置几个救援出口,以及不同最少救援出口的设置方案总数。

思路:首先求割点;那么删掉割点后将形成若干连通块。若某连通块只与一个割点相连那么此连通块内必须取一个点作为救援口;否则不必取。至于方案数,就是取点的块的大小乘积。若不存在割点,那么救援口至少取两个,方案数为n*(n-1)/2(n为图中点的个数)。

 

map<int,int> mp;int n,m;vector<int> g[N];int dfn[N],low[N],cut[N],id;int visit[N];void DFS(int u,int pre){    dfn[u]=low[u]=++id; visit[u]=1;     int cnt=0,i,v;    FOR0(i,SZ(g[u]))    {        v=g[u][i];        if(v==pre) continue;        if(!dfn[v])        {            DFS(v,u); cnt++; upMin(low[u],low[v]);            if(dfn[u]<=low[v]&&pre!=-1) cut[u]=1;        }        else upMin(low[u],dfn[v]);    }    if(cnt>1&&pre==-1) cut[u]=1;}int S[N],color,p[N],mark[N];void dfs(int u){    visit[u]=1; S[color]++;    int i,v;    FOR0(i,SZ(g[u]))    {        v=g[u][i];        if(!visit[v])        {            if(!cut[v]) dfs(v);            else if(mark[v]!=color)            {                mark[v]=color;                p[color]++;            }        }    }}void deal(){    clr(visit,0); clr(S,0); clr(p,0); clr(mark,0);    i64 ans=1,cnt=0;    int i;    color=0;    FOR1(i,m) if(!visit[i]&&!cut[i])    {        color++; dfs(i);        if(p[color]==1) cnt++,ans*=S[color];    }    if(color==1) cnt=2,ans=m*(m-1)/2;    printf("%lld %lld\n",cnt,ans);}int main(){    int num=0;    Rush(n)    {        if(!n) break;        int i;        FOR1(i,n+n) g[i].clear();        mp.clear();        int x,y;        m=0;        FOR1(i,n)        {            RD(x,y);            if(!mp.count(x)) mp[x]=++m;            if(!mp.count(y)) mp[y]=++m;            x=mp[x]; y=mp[y];            g[x].pb(y);            g[y].pb(x);        }        clr(visit,0); id=0; clr(dfn,0); clr(cut,0);        FOR1(i,m) if(!visit[i]) DFS(i,-1);        printf("Case %d: ",++num);        deal();    }}