BUSINESS - Mining your own business 求點雙(細節)
題目描述
John Digger is the owner of a large illudium phosdex mine. The mine is made up of a series of tunnels that meet at various large junctions. Unlike some owners, Digger actually cares about the welfare of his workers and has a concern about the layout of the mine. Specifically, he worries that there may a junction which, in case of collapse, will cut off workers in one section of the mine from other workers (illudium phosdex, as you know, is highly unstable). To counter this, he wants to install special escape shafts from the junctions to the surface. He could install one escape shaft at each junction, but Digger doesn't care about his workers that much. Instead, he wants to install the minimum number of escape shafts so that if any of the junctions collapses, all the workers who survive the junction collapse will have a path to the surface.
Write a program to calculate the minimum number of escape shafts and the total number of ways in which this minimum number of escape shafts can be installed.
輸入格式
The input consists of several test cases. The first line of each case contains a positive integer N (N5 ^{ . } 10 4 ^{4} 4 ) indicating the number of mine tunnels. Following this are N lines each containing two distinct integers s and t, where s and t are junction numbers. Junctions are numbered consecutively starting at 1. Each pair of junctions is joined by at most a single tunnel. Each set of mine tunnels forms one connected unit (that is, you can get from any one junction to any other).
The last test case is followed by a line containing a single zero.
輸出格式
For each test case, display its case number followed by the minimum number of escape shafts needed for the system of mine tunnels and the total number of ways these escape shafts can be installed. You may assume that the result fits in a signed 64-bit integer.
Follow the format of the sample output.
題意翻譯
有一座底下的稀有金屬礦由n條隧道和一些連線點組成,其中每條隧道連線兩個連線點。任意兩個連線點之間最多隻有一條隧道。為了降低礦工的危險,你的任務是在一些連線點處安裝太平井,使得無論哪個連線點倒塌,不在此連線點的所有礦工都能到達太平井逃生(假定除了倒塌的連線點外,其他的連線點和隧道完好無損)。為了節約成本,你應當在儘量少的連線點安裝太平井,還需要計算當太平井的數目最少時的安裝方案總數。 【輸入格式】 輸入包含多組資料,第一行為隧道的條數n(n <= 50 000),以下為n行,每行兩個整數,即一條隧道兩端的連線點編號(所有連線點從1開始標號)。每組資料的所有連線點保證聯通。 【輸出格式】 對於每組資料,輸出兩個整數,即最少需要安裝的太平井的數目以及對應的方案總數。方案總數保證在64位帶符號整數的範圍內。
輸入輸出樣例
輸入 #1
9
1 3
4 1
3 5
1 2
2 6
1 5
6 3
1 6
3 2
6
1 2
1 3
2 4
2 5
3 6
3 7
0
輸出 #1
Case 1: 2 4
Case 2: 4 1
注意:
- 題目中說了可能圖並不連通,所以我們可能要求很多次Tarjan函式,但是不要忘記每次從主函式進入的時候,一定把棧清空,即top=0。
然後最後就是分著求每一個點雙對結果的貢獻。 - 該點雙有0個割點則最少應有2個逃生點,方案數\(size*(size-1)\)
- 該點雙有1個割點則最少應有1個逃生點,方案數\(size-1\)
- 該點雙有2個及以上割點則最少有0個逃生點,該點雙也就無方案數。
- 最後將逃生點數相加即總逃生點數,根據分步乘法原理將各點雙方案數相乘,對於沒有方案的就不乘(而且初始時是1)。
Code
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <vector>
using namespace std;
typedef long long ll;
const int maxn=5e4+5,maxm=1e5+5;
struct Edge{
int next,to;
}e[maxm];
int n,m,rt,Time,head[maxn],low[maxn],dfn[maxn],sta[maxn],top,dcnt,cc;
bool cut[maxn];
vector<int> dcc[maxn];
inline long long rd(){
register int x=0,f=0;register char ch=getchar();
while(ch<'0'||ch>'9')f|=ch=='-',ch=getchar();
while(ch>='0'&&ch<='9')x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
return f?-x:x;
}
void A(int x,int y){
e[++head[0]].to=y;
e[head[0]].next=head[x];
head[x]=head[0];
}
void T(int u){
low[u]=dfn[u]=++Time;
sta[++top]=u;
int cd=0;
for(int i=head[u];i;i=e[i].next){
int v=e[i].to;
if(!dfn[v]){
T(v);
low[u]=min(low[u],low[v]);
if(low[v]>=dfn[u]){
++cd;
if(u!=rt||cd>1)cut[u]=1;
dcnt++;
int x;
do{
x=sta[top--];
dcc[dcnt].push_back(x);
}while(x!=v);
dcc[dcnt].push_back(u);
}
}
else low[u]=min(low[u],dfn[v]);
}
}
void Init(){
memset(dfn,0,sizeof dfn);
memset(low,0,sizeof low);
for(int i=1;i<=dcnt;++i)dcc[i].clear();
memset(cut,0,sizeof cut);
memset(head,0,sizeof head);
memset(sta,0,sizeof sta);
Time=0;dcnt=0;
}
int main(){
int Case=0;
while(scanf("%d",&m)!=EOF&&m){
Init();
for(int i=1,x,y;i<=m;++i){
x=rd();y=rd();
n=max(n,max(x,y));
if(x==y)continue;
A(x,y);A(y,x);
}
for(int i=1;i<=n;++i)if(!dfn[i])top=0,rt=i,T(i);
ll ans=0,ccc=1;
if(dcnt==1) printf("Case %d: 2 %lld\n",++Case,(ll)dcc[1].size()*(dcc[1].size()-1)/2);
else{
for(int i=1;i<=dcnt;++i){
int cc=0;ll sz=(ll)dcc[i].size();
for(int j=0;j<sz;++j) if(cut[dcc[i][j]])cc++;
if(cc==0)ans+=2,ccc*=(sz*(sz-1))/2;
else if(cc==1)ans++,ccc*=(sz-1);
}
printf("Case %d: %lld %lld\n",++Case,ans,ccc);
}
}
return 0;
}