洛谷P3225 [HNOI2012]矿场搭建 割点

本文介绍了一种算法,用于解决在无向图中寻找最小数量的避难所位置问题,确保即使任意一点被破坏,其余点仍可达避难所。通过识别割点和连通块,算法详细阐述了不同情况下避难所的放置策略。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

给出 n ≤ 1 e 3 n\leq1e3 n1e3个点的无向图,你可以在每个点选择放置避难所。然后求问所有的方案满足:1.对于任何一点被破坏,剩下的点能都走到避难所。2.放置避难所的数目最少。
如果这个点放置在非割点的位置,那么不会对图的连通性产生影响。
所以先求出图中所有的割点,这样与每个割点相连接的就是一个连通块。
1.如果这个连通块本身与 0 0 0个割点相连,必须要放置 2 2 2个。
2.如果这个连通块本身与 1 1 1个割点相连,任选一个位置放置。
3.如果这个连通块本身与超过 1 1 1个割点相连,不必放置。
分类统计答案。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int inf=0x3f3f3f3f;
const int N=1e3+7; 
vector<int> go[N]; 
int dfn[N],low[N],tim=0;
bool cut[N];
int vis[N];
int T=0;
void clear() {
	for(int i=1;i<=1000;i++)
		go[i].clear();
	memset(dfn,0,sizeof(dfn));
	memset(low,0,sizeof(low));
	memset(cut,0,sizeof(cut)); 
	tim=0;
	memset(vis,0,sizeof(vis));
	T=0;
}
void tarjan(int u,int rt) {
	dfn[u]=low[u]=++tim; 
	int ch=0;
	for(auto &v:go[u]) {
		if(!dfn[v]) {
			tarjan(v,rt);
			low[u]=min(low[u],low[v]);
			if(low[v]>=dfn[u]&&u!=rt) cut[u]=1;
			if(u==rt) ++ch; 
		}
		low[u]=min(low[u],dfn[v]); 
	}
	if(u==rt&&ch>=2) cut[u]=1;
}
int sz=0,cu=0;
void dfs(int u) {
	vis[u]=T;
	if(cut[u]) { ++cu; return; }
	else ++sz; 
	for(auto &v:go[u]) {
		if(vis[v]!=T) 
			dfs(v);
	}
}
int kase=0; 
int main() {
	int m,n=0;
	while(scanf("%d",&m)!=EOF) {
		n=0;
		if(m==0) break;
		clear();
		for(int i=1;i<=m;i++) {
			int u,v;
			scanf("%d%d",&u,&v);
			go[u].push_back(v);
			go[v].push_back(u);
			n=max(n,u),n=max(n,v);
		}
		for(int i=1;i<=n;i++) {
			if(!dfn[i]) {
				tarjan(i,i);
			}
		}
		ll ans1=0,ans2=1;
		for(int i=1;i<=n;i++) {
			if(!cut[i]&&!vis[i]) {
				T++;
				sz=cu=0;
				dfs(i); 
				if(cu==0) ans1+=2,ans2*=1ll*sz*(sz-1)/2;
				else if(cu==1) ans1+=1,ans2*=sz;
			}
		}	
		printf("Case %d: %lld %lld\n",++kase,ans1,ans2); 
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值