题目大意
给定一个由隧道连接挖煤点的无向图,保证在任何一个工地发生坍塌后,其他的挖煤点的工人都有一条道路通向救援出口
求最少的救援出口处和总方案数
问题求解
显然
如果没有割点
至少需要建立两个出口
从任意非割点的地方选择两个点建立
如果这个分组只有一个割点
只需要在分组内设立一个出口
可以设立在任意一个非割点的地方
如果有两个及以上个割点,则无需建立,可以直接到达其他联通块
代码实现
#include<cstdio>
#include<vector>
#include<stack>
#include<cstring>
using namespace std;
typedef long long LL;
const int maxn=505;
int T,N,M,K,cnt,Ans1,c[maxn],low[maxn],dfn[maxn],root;
LL Ans2;
vector<int> e[maxn],DCC[maxn];
stack<int> s;
inline int read(){
int ret=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-f;ch=getchar();}
while(ch<='9'&&ch>='0')ret=ret*10+ch-'0',ch=getchar();
return ret*f;
}
void Tarjan(int x){
int fa=0; s.push(x);
low[x]=dfn[x]=++cnt;
for(int j=0;j<e[x].size();j++){
int y=e[x][j];
if(!dfn[y]){
Tarjan(y);
low[x]=min(low[x],low[y]);
if(low[y]>=dfn[x]){
fa++;K++;
if(x!=root||fa>1)c[x]=1;
DCC[K].push_back(x);
int z=0;
do{
DCC[K].push_back(z=s.top());s.pop();
}while(z!=y);
}
}
else low[x]=min(low[x],dfn[y]);
}
}
int main(){
freopen("P3225.in","r",stdin);
freopen("P3225.out","w",stdout);
while(true){
N=read();if(!N)break;
while(!s.empty()) s.pop();
for(int i=1;i<=N;i++) e[i].clear();
for(int i=1;i<=K;i++) DCC[i].clear();
M=K=Ans1=cnt=0;Ans2=1;
memset(low,0,sizeof low);
memset(c,0,sizeof c);
memset(dfn,0,sizeof dfn);
for(int i=1;i<=N;i++){
int x=read(),y=read();
e[x].push_back(y);e[y].push_back(x);
M=max(M,x);M=max(M,y);
}
for(int i=1;i<=M;i++)if(!dfn[i]){
root=i;Tarjan(i);
}
for(int i=1;i<=K;i++){
int num=0,cnt=DCC[i].size();
for(int j=0;j<cnt;j++)num+=c[DCC[i][j]];
// for(int j=0;j<cnt;j++)printf("%d ",DCC[i][j]);
printf("
");
if(num==1)Ans1++,Ans2*=cnt-1;
if(num==0)Ans1+=2,Ans2*=cnt*(cnt-1)>>1;
}
// printf("%d %lld
",Ans1,Ans2);
printf("Case %d: %d %lld
",++T,Ans1,Ans2);
}
return 0;
}
关于其他
貌似有三倍经验???