洛谷 P3225 矿场搭建
题意
煤矿工地可以看成是由隧道连接挖煤点组成的无向图。为安全起见,希望在工地发生事故时所有挖煤点的工人都能有一条出路逃到救援出口处。于是矿主决定在某些挖煤点设立救援出口,使得无论哪一个挖煤点坍塌之后,其他挖煤点的工人都有一条道路通向救援出口。
请写一个程序,用来计算至少需要设置几个救援出口,以及不同最少救援出口的设置方案总数。
思路
求出所有的点双连通分量。
如果这个分量内没有割点,则需要两个出口,任意设置即可,\(C_n^2\) 种情况。
如果这个分量内有一个割点,任意设置一个不在各点出口即可。
如果出口没塌,这个分量内所有点都能跑到这里。
如果出口塌了,则割点一定没塌,可以跑到其他分量的出口去。
如果这个分量内有两个及以上的割点,则不用设置出口,因为无论哪个点塌了都能跑到其他分量去。
代码
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int M=1005;
int n,m,ver[M<<1],nxt[M<<1],head[M],tot,low[M],dfn[M],vis[M],gr,num,cut,cnt,ans0,ans1;
bool isg[M];
void clear(){
n=0,m=0,tot=0,cnt=0,ans0=0,ans1=1,gr=0,num=0,cut=0;
memset(low,0,sizeof(low));
memset(dfn,0,sizeof(dfn));
memset(ver,0,sizeof(ver));
memset(nxt,0,sizeof(nxt));
memset(head,0,sizeof(head));
memset(isg,0,sizeof(isg));
memset(vis,0,sizeof(vis));
}
void add(int x,int y){
ver[++tot]=y;
nxt[tot]=head[x];
head[x]=tot;
}
void tarjan(int x,int fa){
low[x]=dfn[x]=++cnt;
int sum=0;
for(int i=head[x];i;i=nxt[i]){
int v=ver[i];
if(!dfn[v]){
tarjan(v,x);
low[x]=min(low[x],low[v]);
if(fa!=0&&low[v]>=dfn[x])isg[x]=1;
else sum++;
}else if(v!=fa){
low[x]=min(low[x],dfn[v]);
}
}
if(fa==0&&sum>=2)isg[x]=1;
}
void dfs(int x){
vis[x]=gr;
num++;
for(int i=head[x];i;i=nxt[i]){
int v=ver[i];
if(isg[v]&&vis[v]!=gr){
cut++;
vis[v]=gr;
}
if(!vis[v])dfs(v);
}
}
bool solve(int id){
clear();
scanf("%lld",&m);
if(m==0)return false;
for(int i=1,u,v;i<=m;i++){
scanf("%lld%lld",&u,&v);
add(u,v);
add(v,u);
n=max(n,u);
n=max(n,v);
}
for(int i=1;i<=n;i++){if(!dfn[i])tarjan(i,0);}
for(int i=1;i<=n;i++){
if(!vis[i]&&!isg[i]){
++gr;
num=cut=0;
dfs(i);
if(cut==0){
ans0+=2;
ans1*=num*(num-1)/2;
}else if(cut==1){
ans0++;
ans1*=num;
}
}
}
printf("Case %lld: %lld %lld\n",id,ans0,ans1);
return true;
}
signed main(){
int id=0;
while(solve(++id));
return 0;
}
标签:洛谷,矿场,int,割点,出口,vis,挖煤,P3225,分量
From: https://www.cnblogs.com/maniubi/p/18393849