版权声明:原创,未经作者允许禁止转载 https://blog.csdn.net/Mr_wuyongcong/article/details/88717355
正题
题目链接:https://www.luogu.org/problemnew/show/P3225
题目大意
个点的无向图,要求设置逃生点使得任意一个点去掉后每联通分量内都有一个逃生点。求至少多少个逃生点和方案数。
解题思路
首先
求出割点,然后对于一个分量内没有割点,那么就得设置两个点,如果有一个割点,就要设置一个。
然后方案数直接用联通分量内的点数来求。
#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);
}
}