P3225-[HNOI2012]矿场搭建【tarjan,图论】

版权声明:原创,未经作者允许禁止转载 https://blog.csdn.net/Mr_wuyongcong/article/details/88717355

正题

题目链接:https://www.luogu.org/problemnew/show/P3225


题目大意

n n 个点的无向图,要求设置逃生点使得任意一个点去掉后每联通分量内都有一个逃生点。求至少多少个逃生点和方案数。


解题思路

首先 t a r j a n tarjan 求出割点,然后对于一个分量内没有割点,那么就得设置两个点,如果有一个割点,就要设置一个。
然后方案数直接用联通分量内的点数来求。


c o d e code

#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 500
#define ll long long
using namespace std;
struct line{
    ll to,next;
}a[N*N];
ll tot,ls[N],v[N],dfn[N],low[N],num,Case;
ll n,cut[N],ans1,ans2,m,sonn,grade,cnt,Cut;
void addl(ll x,ll y)
{
    a[++tot].to=y;
    a[tot].next=ls[x];
    ls[x]=tot;
}
void tarjan(ll x,ll fa)
{
    dfn[x]=low[x]=++num;
    ll L=0;
    for(ll i=ls[x];i;i=a[i].next){
        ll y=a[i].to;
        if(!dfn[y]){
            tarjan(y,x);
            low[x]=min(low[y],low[x]);
            if(dfn[x]<=low[y])
            {
                cut[x]=1;
                if(fa==0) sonn++;
			}
        }
        else if(y!=fa)
          low[x]=min(dfn[y],low[x]);
    }
}
void dfs(ll x)
{
    v[x]=grade;
    cnt++;
    for(ll i=ls[x];i;i=a[i].next)
    {
        ll y=a[i].to;
        if(cut[y]&&v[y]!=grade)
        {
            Cut++;
            v[y]=grade;
        }
        if(!v[y]) dfs(y);
    }
}
int main()
{
    while(1)
    {
        memset(cut,0,sizeof(cut));
        memset(dfn,0,sizeof(dfn));
        memset(low,0,sizeof(low));
        memset(ls,0,sizeof(ls));
        memset(v,0,sizeof(v));
        Case++;
     	tot=grade=ans1=grade=num=n=0;
     	ans2=1;
      	scanf("%lld",&m);
      	if(!m) break;
      	for (ll i=1;i<=m;i++)
      	{
    		ll x,y;
        	scanf("%lld%lld",&x,&y);
        	n=max(n,max(x,y));
        	addl(x,y);addl(y,x);
      	}
      	for(ll i=1;i<=n;i++)
      	{
    		if(dfn[i]) continue;
    		sonn=0;
    		tarjan(i,0);
    		if(sonn<2) cut[i]=0;
     	}
      	for(ll i=1;i<=n;i++)
      	{
            if(!v[i]&&!cut[i])
            {
                grade++;cnt=Cut=0;
                dfs(i);
                if(Cut==0){
                    ans1+=2;
                    ans2*=(cnt-1)*cnt/2;
                }
                else if(Cut==1){
                    ans1+=1;
                    ans2*=cnt;
                }
            }
     	}
      	printf("Case %lld: %lld %lld\n",Case,ans1,ans2);
    }
}

猜你喜欢

转载自blog.csdn.net/Mr_wuyongcong/article/details/88717355
今日推荐