BZOJ 2730 矿场搭建 Tarjan求割点

思路:
Tarjan求出来点双&割点 判一判就行了

//By SiriusRen
#include <stack>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
#define int long long
#define N 66666
#define mem(x,y) memset(x,y,sizeof(x))
stack<int>stk;
int first[666],next[N],v[N],rec[666][666],dfn[666],low[666],tot,all,cnt;
int xx,yy,root,n,m,Point[666],ans1,ans2,cases;
void add(int x,int y){v[tot]=y,next[tot]=first[x],first[x]=tot++;}
void tarjan(int x,int fa){
    dfn[x]=low[x]=++cnt;int num=0;
    for(int i=first[x];~i;i=next[i]){
        if(i==fa)continue;
        if(!dfn[v[i]]){
            stk.push(i),tarjan(v[i],i^1);
            low[x]=min(low[x],low[v[i]]);
            if(dfn[x]<=low[v[i]]){
                all++;num++,rec[all][++rec[all][0]]=x;
                do{
                    xx=stk.top();stk.pop();
                    rec[all][++rec[all][0]]=v[xx];
                }while(xx!=i);
            }
        }
        else low[x]=min(low[x],dfn[v[i]]);
    }
    if(((x==root)&&num>=2)||(x!=root&&num))Point[x]=1;
}
signed main(){
    while(~scanf("%lld",&m)&&m){
        mem(first,-1),mem(Point,0),mem(dfn,0),ans1=n=all=tot=0,ans2=1;
        for(int i=1;i<=m;i++){
            scanf("%lld%lld",&xx,&yy);
            add(xx,yy),add(yy,xx);
            n=max(n,max(xx,yy)),rec[i<<1][0]=rec[(i<<1)-1][0]=0;
        }
        for(int i=1;i<=n;i++)
            if(!dfn[i])root=i,tarjan(i,-1);
        for(int i=1;i<=all;i++){
            int num=0;
            for(int j=1;j<=rec[i][0];j++)
                if(Point[rec[i][j]])num++;
            if(num==1)ans1++,ans2*=(rec[i][0]-1);
            else if(!num){
                if(rec[i][0]==1)ans1++;
                else ans1+=2,ans2=ans2*rec[i][0]*(rec[i][0]-1)/2; 
            }
        }
        printf("Case %lld: %lld %lld\n",++cases,ans1,ans2);
    }
}

这里写图片描述

posted @ 2016-11-05 07:37  SiriusRen  阅读(133)  评论(0编辑  收藏  举报