Luogu_P3225 [HNOI2012]矿场搭建 割点

Luogu_P3225 [HNOI2012]矿场搭建

割点


题目链接
这种把点拿走明显就和割点有关系
那么就先求割点
求完割点然后???
首先dfs每个割点分开的连通块
如果这个连通块没有割点,必然要两个出口
假如有这个联通有一个割点那么一定要放一个,因为假如割点塌了就需要这个点了
假如有两个或者更多那么就不需要了,因为哪怕塌了割点也可以从别的割点走


代码如下:

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=1010;
int tim,n,m,head[maxn],tot,cnt,low[maxn],dfn[maxn],cut[maxn],rt,vis[maxn],cct,siz,cs;
struct node{
    int nxt,to;
    #define nxt(x) e[x].nxt
    #define to(x) e[x].to
}e[maxn<<1];
inline void add(int from,int to){
    to(++tot)=to;nxt(tot)=head[from];head[from]=tot;
}
void tarjan(int x){
    dfn[x]=low[x]=++cnt;
    int fl=0;
    for(int i=head[x];i;i=nxt(i)){
        int y=to(i);
        if(!dfn[y]){
            tarjan(y);
            low[x]=min(low[x],low[y]);
            if(low[y]>=dfn[x]){
                fl++;
                if(x!=rt || fl>1) cut[x]=1;
            }
        }else low[x]=min(low[x],dfn[y]);
    }
}
void dfs(int x){
    vis[x]=tim;
    if(cut[x]) return;
    siz++;
    for(int i=head[x];i;i=nxt(i)){
        int y=to(i);
        if(cut[y] && vis[y]!=tim) cct++,vis[y]=tim;
        if(!vis[y]) dfs(y);
    }
}
int main()
{
    while(scanf("%d",&m) && m){
        memset(head,0,sizeof(head));memset(e,0,sizeof(e));siz=tim=cnt=n=tot=cct=0;
        memset(low,0,sizeof(low));memset(dfn,0,sizeof(dfn));memset(cut,0,sizeof(cut));memset(vis,0,sizeof(vis));
        for(int x,y,i=1;i<=m;i++){
            scanf("%d%d",&x,&y);add(x,y);add(y,x);n=max(n,y);n=max(n,x);
        }
        for(int i=1;i<=n;i++) if(!dfn[i]) rt=i,tarjan(i);
        ll ans1=0,ans2=1;
        for(int i=1;i<=n;i++)
            if(!vis[i] && !cut[i]){
                tim++;cct=siz=0;
                dfs(i);
                if(!cct)
                    ans1+=2,ans2*=siz*(siz-1)/2;
                if(cct==1)
                    ans1++,ans2*=siz;
            }
        printf("Case %d: %lld %lld
",++cs,ans1,ans2);
    }
    return 0;
}
原文地址:https://www.cnblogs.com/ChrisKKK/p/11652712.html