AcWing 396. 矿场搭建
懒得写了,直接照搬大佬的题解
#include<bits/stdc++.h>
using namespace std;
typedef unsigned long long ULL;
const int N = 1010, M = 510;
int h[N], e[M], ne[M], idx;
int n ,m;
int low[N], dfn[N], timep;
vector<int>dcc[N]; //储存分割块内都有那些点
bool cut[N]; //记录是不是割点
int root;
int dcc_cnt; //记录分割块数量
int stk[N];
int top;
void add(int a, int b){
e[idx] = b;
ne[idx] = h[a];
h[a] = idx ++ ;
}
void tarjan(int u){
low[u] = dfn[u] = ++ timep;
stk[ ++ top] = u;
//如果是孤点
if(u == root && h[u] == -1){
dcc_cnt ++ ; //分割块数量++
dcc[dcc_cnt].push_back(u);
return ;
}
//如果不是孤点
int cnt = 0;
for(int i = h[u]; ~i; i = ne[i]){
int j = e[i];
if(!dfn[j]){
tarjan(j);
low[u] = min(low[u], low[j]);
if(dfn[u] <= low[j]){
cnt ++ ;
if(u != root || cnt > 1) cut[u] = true; //是个割点
++ dcc_cnt;
int y;
do{
y = stk[top -- ];
dcc[dcc_cnt].push_back(y);
}while(y != j); //因为要保留u给其他包含u的分割块,所以不将u弹出
dcc[dcc_cnt].push_back(u); //这个分割块内还是要加入u节点
}
}
else low[u] = min(low[u], dfn[j]);
}
}
int main()
{
int T = 1;
while(scanf("%d", &m), m){
for(int i = 1; i <= dcc_cnt; i ++ ) dcc[i].clear(); //分割块的数量是从1开始用的
n = idx = timep = dcc_cnt = top = 0;
memset(h, -1, sizeof h);
memset(dfn, 0, sizeof dfn);
memset(cut, 0, sizeof cut);
while(m -- ){
int a, b;
cin>>a>>b;
n = max(n, max(a, b));
add(a, b);
add(b, a);
}
for(root = 1; root <= n; root ++ ){
if(!dfn[root])
tarjan(root);
}
//遍历每一个分割块
int res = 0;
ULL num = 1; //记录方案数
for(int i = 1 ;i <= dcc_cnt; i ++ ){
int cnt = 0; //记录分割点数
for(int j = 0; j < dcc[i].size(); j ++ ){
if(cut[dcc[i][j]]) cnt ++ ;
}
if(cnt == 0){ //如果没有割点
if(dcc[i].size() > 1) res += 2, num *= dcc[i].size() * (dcc[i].size() - 1) / 2; //这不是个孤点
else res ++ ;
}
else if(cnt == 1) res ++ , num *= dcc[i].size() - 1;
}
printf("Case %d: %d %llu\n", T ++, res, num);
}
return 0;
}