洛谷 3225矿场搭建

一道无向图tarjan的题

大致思路是先找到割点,然后给连通块染色

分三种情况:

如果连通块有一个割点,那么建在哪都行

如果有两个割点那么无需建出口,因为任意一个割点倒塌都可以从另一个割点跑到另一个连通块

如果没有割点的话则无法跑到其他连通块,在除割点的任意两点建两个连通块

#include<iostream>
#include<cstdio>
#include<cstring>
#define mx 505
using namespace std;
struct edge{
    int to,nxt;
}ed[2*mx];
int head[mx],ecnt;
void addedge(int u,int v){
    ed[++ecnt].to=v;
    ed[ecnt].nxt=head[u];
    head[u]=ecnt;
}
long long root,mark,cnt,siz,numcase;
long long low[mx],dfn[mx],num;
long long col,color[mx];
bool dot[mx];
void tarjan(int u,int fa){
    low[u]=dfn[u]=++num;
    for(int i=head[u];i;i=ed[i].nxt){
        int v=ed[i].to;
        if(!dfn[v]){
            tarjan(v,u);
            low[u]=min(low[u],low[v]);
            if(low[v]>=dfn[u]){
                if(u==root) mark++;
                else dot[u]=1;
            }
        }
        else if(v!=fa) low[u]=min(low[u],dfn[v]);
    }
}
void get_col(int u){
    color[u]=col;
    siz++;
    for(int i=head[u];i;i=ed[i].nxt){
        int v=ed[i].to;
        if(color[v]!=col&&dot[v]){
            cnt++;
            color[v]=col;
        }    
        if(!color[v]) get_col(v);
    }
}
long long ans1,ans2;
void init(){
    memset(head,0,sizeof head);
    memset(ed,0,sizeof ed);
    memset(color,0,sizeof color);
    memset(dfn,0,sizeof dfn);
    memset(low,0,sizeof low);
    memset(dot,0,sizeof dot);
    col=mark=ans1=0;
    ans2=1;
}
int main(){
    int n=0,m;
    while(scanf("%d",&m)&&m){
        n=0;
        numcase++;
        init();//多组数据初始化 
        for(int i=1;i<=m;i++){
            int s,t;
            scanf("%d%d",&s,&t);
            addedge(s,t);
            addedge(t,s);
            n=max(n,max(s,t));//此处求点的数量 
        }
        for(int i=1;i<=n;i++){
            if(!dfn[i]) {
                mark=0;
                tarjan(root=i,-1);
                if(mark>=2)  dot[root]=1; 
            }
        }
        for(int i=1;i<=n;i++)
            if(!color[i]&&!dot[i])
            {
                cnt=0,siz=0;//cnt表示割点数量,siz表示非割点数量 
                col++;
                get_col(i);
                if(cnt==1){
                    ans1+=1;
                    ans2*=siz;
                }
                if(cnt==0){
                    ans1+=2;
                    ans2*=((siz-1)*siz)/2;//除以2是因为选择a,b和b,a是等价的 
                }
            }
        printf("Case %lld: %lld %lld\n",numcase,ans1,ans2);//注意数据范围是long long 
    }    
    return 0;
}

 

上一篇:登录


下一篇:每日学习