题目链接:
https://www.luogu.org/problem/P3225
参考博客:
https://www.luogu.org/blog/cjyyb/solution-p3225
这道题目的思路完全是根据博主所讲,画图模拟每一种情况最终才理解,写出了自己的代码
思路:
1:tarjan求出割点集合
2:割点把原图分为了若干联通块,(去掉割点和割点相连,拆分后的每一个联通子块)
3:分类讨论画图模拟各种情况
1:每一个联通子块:如果没有割点,至少需要建立两个出口,从任意非割点的地方选择两个点建立
2:每一个联通子块:如果只有一个割点,只需要在分组内设立一个出口,可以设立在任意一个非割点的地方
3:每一个联通子块:如果有两个及以上个割点,则无需建立,可以直接到达其它联通块
#include<bits/stdc++.h>
using namespace std;
const int maxn=1e3+1;
vector<int>e[maxn];
int dfn[maxn],low[maxn],timing,n,m,x,y,Case;
set<int>s;
int vis[maxn],group,cnt,num,ans1;
long long ans2;
void tarjan(int u,int root)
{
low[u]=dfn[u]=++timing;
int child=0;
for(int i=0;i<e[u].size();i++)
{
int v=e[u][i];
if(dfn[v]==0)
{
child++;
tarjan(v,root);
low[u]=min(low[u],low[v]);
if(u!=root&&low[v]>=dfn[u])
{
s.insert(u);
}
}
low[u]=min(low[u],dfn[v]);
}
if(u==root&&child>1)
{
s.insert(u);
}
}
void dfs(int u)
{
vis[u]=group;
num++;
for(int i=0;i<e[u].size();i++)
{
int v=e[u][i];
if(s.count(v)&&vis[v]!=group)
{
cnt++;
vis[v]=group;
}
if(!vis[v])
{
dfs(v);
}
}
}
void init()
{
ans1=0;
ans2=1;
memset(dfn,0,sizeof(dfn));
memset(low,0,sizeof(low));
memset(vis,0,sizeof(vis));
timing=n=group=0;
for(int i=1;i<=maxn;i++)
{
e[i].clear();
}
s.clear();
}
int main()
{
ios::sync_with_stdio(0);
while(1)
{
cin>>m;
if(m==0)break;
init();
for(int i=1;i<=m;i++)
{
cin>>x>>y;
n=max(n,x);
n=max(n,y);
e[x].push_back(y);
e[y].push_back(x);
}
for(int i=1;i<=n;i++)
{
if(dfn[i]==0)
tarjan(i,i);
}
for(int i=1;i<=n;i++)
{
if(!vis[i]&&!s.count(i))
{
++group;
num=cnt=0;
dfs(i);
if(cnt==0)
{
ans1+=2;
ans2*=num*(num-1)/2;
}
else if(cnt==1)
{
ans1+=1;
ans2*=num;
}
else if(cnt>=2)
{
}
}
}
cout<<"Case "<<++Case<<": "<<ans1<<" "<<ans2<<endl;
}
return 0;
}