[HNOI2012]矿场搭建 割点
题目
[HNOI2012]矿场搭建(https://ac.nowcoder.com/acm/problem/20099)
题目描述
煤矿工地可以看成是由隧道连接挖煤点组成的无向图。为安全起见,希望在工地发生事故时所有挖煤点的工人都能有一条出路逃到救援出口处。
于是矿主决定在某些挖煤点设立救援出口,使得无论哪一个挖煤点坍塌之后,其他挖煤点的工人都有一条道路通向救援出口。
请写一个程序,用来计算至少需要设置几个救援出口,以及不同最少救援出口的设置方案总数。
输入描述:
输入文件有若干组数据,每组数据的第一行是一个正整数 N(N ≤ 500),表示工地的隧道数,
接下来的N行每行是用空格隔开的两个整数S和T,表示挖S与挖煤点T由隧道直接连接。
输入数据以 0 结尾。
输出描述:
输入文件中有多少组数据,输出文件 output.txt 中就有多少行。
每行对应一组输入数据的结果。
其中第 i 行以 Case i: 开始(注意大小写,Case 与 i 之间有空格,i 与:之间无空格,: 之后有空格),其后是用空格隔开的两个正整数,
第一个正整数表示对于第 i 组输入数据至少需 要设置几个救援出口,
第二个正整数表示对于第 i 组输入数据不同最少救援出口的设置方案总数。
输入数据保证答案小于 2^64。输出格式参照以下输入输出样例。
输入
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
输出
Case 1: 2 4
Case 2: 4 1
思路
对于任何一个联通块,如果坍塌的是一个联通块中的割点的话,那么分割成的每个小联通块中必须保证各有一个出口。
我们考虑所有的割点将原图分割成若干个小联通块接下来分类讨论:
(1)小块不与任何一个割点相连,那我们需要在这里设立两个出口,以保证任何一个出口坍塌后,还有一个出口可用。
(2)小块只与一个割点相连,那么我们只需要设立一个即可,出口没坍塌或割点坍塌了就直接用,如果出口坍塌了,那则可以通过没坍塌的割点去另一个小块的出口。
(3)小块与超过一个割点相连,也就意味着,无论小块中的那个点塌了,都可以通过其他的点到另一个小块的出口。
#include<bits/stdc++.h>
#define LL long long
using namespace std;
struct Egde {
int from, to, nxt;
} e[100005];
int head[505], cut=0, T=0;
int vis[505];
struct Edgescc {
int dfn[505], low[505], pre[505];
void init() {
memset(dfn, 0, sizeof(dfn));
memset(head, -1, sizeof(head));
memset(vis, 0, sizeof(vis));
memset(pre, -1, sizeof(pre));
cut=T=0;
}
void Addedge(int x, int y) {
e[cut]= {x, y, head[x]};
head[x]=cut++;
}
void TA(int u, int fa, int q) {
dfn[u]=low[u]=++T;
int sz=0;
for(int i=head[u]; i!=-1; i=e[i].nxt) {
int to=e[i].to;
if(i==(q^1)) {
continue;
}
if(!dfn[to]) {
sz++;
pre[to]=u;
TA(to, u, i);
low[u]=min(low[u], low[to]);
if(pre[u]==-1&&sz>1&&!vis[u]) {
vis[u]=1;//割点
}
if(pre[u]!=-1&&low[to]>=dfn[u]&&!vis[u]) {
vis[u]=1;//割点
}
}
else{
low[u]=min(low[u], dfn[to]);
}
}
}
} sc;
int sz=0, siz=0;
unordered_map<int, int> mp;
void get(int u){
vis[u]=-1;
siz++;
for(int i=head[u]; i!=-1; i=e[i].nxt){
int to=e[i].to;
if(vis[to]==1){
mp[to]=1;
}
if(vis[to]==0){
get(to);
}
}
}
int main() {
int n, m, cas=1;
while(scanf("%d", &m), m) {
sc.init();
n=0;
for(int i=1; i<=m; i++) {
int x, y;
scanf("%d%d", &x, &y);
n=max(n, max(x, y));
sc.Addedge(x, y);
sc.Addedge(y, x);
}
LL ans1=0, ans2=1;
for(int i=1; i<=n; i++){
if(!sc.dfn[i]){
sc.TA(i, -1, -1);
}
}
for(int i=1; i<=n; i++){
if(vis[i]==0){
sz=siz=0;
mp.clear();
get(i);
sz=mp.size();//这个连通块和多少个割点连接
if(sz==0){
if(siz>1){
ans2*=((1ll*siz)*(siz-1)/2);
ans1+=2;
}
else{
ans1++;
}
}
if(sz==1){
ans1++;
ans2*=siz;
}
}
}
printf("Case %d: %lld %lld\n", cas++, ans1, ans2);
}
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)