井下矿工
问题描述
有一座地下的稀有金属矿由n条隧道和一些连接点组成,其中每条隧道连接两个连接点。任意两个连接点之间最多只有一条隧道。为了降低矿工的危险,你的任务是在一些连接点处安装太平井和相应的逃生装置,使得不管哪个连接点倒塌,不在此连接点的所有矿工都能到达太平井逃生(假定除倒塌的连接点不能通行外,其他隧道和连接点完好无损)。为了节约成本,你应当在尽量少的连接点安装太平井。你还需要计算出在太平井的数目最小时的安装方案总数。
输入格式
输入包含多组数据。每组数据第一行为隧道的条数n(n<=50000),一下n行,每行两个整数,即一条隧道两段的连接点的编号(所有连接点从1开始编号)。每组数据的所有连接点保证连通。
输入以数字0作为结束
输出格式
对于每组数据,输出两个整数,即最少需要安装的太平井数目以及对应的方案总数。
答案保证在64位带符号整数范围以内。
思路
意思就是,给一个简单无向图G,求点p的个数,使得任意一个点及其相关的边被去掉之后,剩下的每一个点都能够到达这些p点。
最简单的情况:G本身就是一个双连通分量,这时安装的太平井数目就是2个。
一般的情况:对于一般的图GG,可以近似认为它是由若干个双连通分量组成的,那么每一个双连通分量内就应该安放两个太平井。显然,割点上面不应该放太平井,如图。如果这个双连通分量内只有两个点,那么其中必定有一个是割点,所以这种情况下只用放一个太平井。
【黄点是割点】
这样一来,这题的思路就很清晰了:找图G内有几个双连通分量,乘以2,然后除去割点的数目,就是太平井的数目;方案数乘起来就可以了。
代码
代码冗杂,仅供对拍
#include<iostream>
#include<cstdio>
#include<stack>
#include<vector>
#define N 100050
#define ll long long
using namespace std;
stack<ll>s;
vector<ll>ve[N];
ll Next[N],Last[N],End[N],St[N],cnt;
ll Low[N],Dfn[N],Time,Son[N],cut[N],bcc,Belong[N];
bool used[N];
void Ins(ll x,ll y){
End[++cnt]=y;
St[cnt]=x;
Next[cnt]=Last[x];
Last[x]=cnt;
}
void Tarjan(ll u,ll fa){
Time++;
Low[u]=Time,Dfn[u]=Time;
for(ll i=Last[u];i;i=Next[i]){
ll v=End[i];
if(!Dfn[v]){
Son[u]++;//注意此处的Son[u]指的是以u为树根的搜索树的儿子,不是G内的父子关系
s.push(i);
Tarjan(v,u);
Low[u]=min(Low[u],Low[v]);
if(Dfn[u]<=Low[v]){
cut[u]=true,bcc++;
ll x,y;
do{
ll k=s.top();s.pop();
x=St[k],y=End[k];
if(Belong[x]!=bcc)
ve[bcc].push_back(x),Belong[x]=bcc;
if(Belong[y]!=bcc)
ve[bcc].push_back(y),Belong[y]=bcc;
}while(!(x==u && y==v));
}
}else if(Dfn[u]>Dfn[v] && v!=fa)
Low[u]=min(Low[u],Dfn[v]),s.push(i);
}
if(!fa && Son[u]<=1)cut[u]=false;
}
void Clean(){
for(ll i=0;i<N;i++){
Next[i]=0,Last[i]=0,End[i]=0,St[i]=0;
Low[i]=0,Dfn[i]=0,Son[i]=0,cut[i]=0;
Belong[i]=0,used[i]=0;
ve[i].clear();
}
while(!s.empty())s.pop();
cnt=0,Time=0,bcc=0;
}
int kkk(){
ll n,t=0;
while(1){
scanf("%lld",&n);
if(!n)break;
ll tot=0;t++;
for(ll i=1;i<=n;i++){
ll x,y;
scanf("%lld%lld",&x,&y);
Ins(x,y),Ins(y,x);
if(!used[x])used[x]=true,tot++;
if(!used[y])used[y]=true,tot++;
}
Tarjan(1,0);
ll Ans1=0,Ans2=1;
for(ll i=1;i<=bcc;i++){
ll c=0,_Size=ve[i].size();
for(ll j=0;j<_Size;j++){
if(cut[ve[i][j]])c++;
}
if(c!=1)continue;
Ans1++,Ans2*=(_Size-1);
}
if(bcc==1)Ans1=2,Ans2=(tot*(tot-1))/2;
printf("Case %lld: %lld %lld\n",t,Ans1,Ans2);
Clean();
}
return 0;
}
//扩栈代码
const int main_stack=16;
char my_stack[128<<20];
int main() {
__asm__("movl %%esp, (%%eax);\n"::"a"(my_stack):"memory");
__asm__("movl %%eax, %%esp;\n"::"a"(my_stack+sizeof(my_stack)-main_stack):"%esp");
kkk();
__asm__("movl (%%eax), %%esp;\n"::"a"(my_stack):"%esp");
return 0; //add stack copy copy!!!
}