题号 NC20099
名称 [HNOI2012]矿场搭建
来源 [HNOI2012]
戳我进入往期每日一题汇总贴~
往期每日一题二期题单
如果你在题库做题时遇到了喜欢的题目,欢迎推荐给邓老师~ 点击查看详情
欢迎给每日一题投稿,投稿需要提供牛客题库里的题目+题解 投稿有牛币奖励,可发站内信给王清楚或联系QQ 234186389
每日一题QQ群:659028468
题解
作者:小熠小熠很不容易
题意
给定一个无向图,问最少在几个点上设置出口,可以使得不管哪个点坍塌,其余所有点都可以与某个出口连通。求设置出口的数量及方案数。
做法:Tarjan,割点
前置芝士:割点
思路:
- 目前可公开情报:
1.孤点:数量+1,方案数不变
2.连通块无割点:数量+2,方案数
3.连通块存在一个割点,数量+1,方案数
(在一个强连通分量中只要建立1个)
4.连通块存在两个及以上割点,数量不变,方案数不变
ps:这题题目并没有考虑有多个连通图,以及孤点的存在,应加强数据。
贴一组数据
1 2 3 6 1 2 1 3 2 4 2 5 3 6 3 8 0 Case 1: 3 1 Case 2: 5 1
代码
#include <bits/stdc++.h>
using namespace std;
#define pb push_back
#define mp(aa,bb) make_pair(aa,bb)
#define _for(i,b) for(int i=(0);i<(b);i++)
#define rep(i,a,b) for(int i=(a);i<=(b);i++)
#define per(i,b,a) for(int i=(b);i>=(a);i--)
#define mst(abc,bca) memset(abc,bca,sizeof abc)
#define X first
#define Y second
#define lowbit(a) (a&(-a))
#define debug(a) cout<<#a<<":"<<a<<"\n"
typedef long long ll;
typedef pair<int,int> pii;
typedef unsigned long long ull;
typedef long double ld;
const int N=1010;
const int INF=0x3f3f3f3f;
const int mod=1e9+7;
const double eps=1e-6;
const double PI=acos(-1.0);
int n,m,cas;
vector<int> g[N];
int dfn[N],low[N],timetag; //dfn[u]:遍历到u的时间 low[u]:u通过有向边可回溯的最早次序(环中最小的dfn)
stack<int> s;
int dcc_cnt;
vector<int> dcc[N];
bool flag[N];
void init(){
n=dcc_cnt=timetag=0;
for(int i=1;i<=1000;i++) g[i].clear(),dcc[i].clear();
mst(dfn,0);mst(low,0);mst(flag,0);
s=stack<int>();
}
void tarjan(int u,int fa){
dfn[u]=low[u]=++timetag;
s.push(u);
if(u==fa&&g[u].empty()){ //孤点
dcc[++dcc_cnt].pb(u);
return;
}
int cnt=0;
for(auto v:g[u]){
if(!dfn[v]){
tarjan(v,u);
low[u]=min(low[u],low[v]); //回溯
if(dfn[u]<=low[v]){
cnt++;
if(u!=fa||cnt>1) flag[u]=1;
dcc[++dcc_cnt].pb(u);
int y;
do{
y=s.top();s.pop();
dcc[dcc_cnt].pb(y);
}while(y!=v);
}
}
else if(fa!=v){ //是不是在这个环中
low[u]=min(low[u],dfn[v]);
}
}
}
void solve(){
init();
while(m--){
int s,t;cin>>s>>t;
g[s].push_back(t);
g[t].push_back(s);
n=max({n,s,t});
}
for(int i=1;i<=n;i++){
if(!dfn[i]) tarjan(i,i);
}
ll ans1=0,ans2=1;
for(int i=1;i<=dcc_cnt;i++){
int cnt=0;
for(auto x:dcc[i]){
if(flag[x]) cnt++;
}
if(!cnt){
if(dcc[i].size()==1) ans1++; //孤点
else ans1+=2,ans2*=dcc[i].size()*(dcc[i].size()-1)/2;
}
else if(cnt==1) ans1++,ans2*=dcc[i].size()-1;
}
cout<<"Case "<<++cas<<": "<<ans1<<" "<<ans2<<"\n";
}
int main(){
ios::sync_with_stdio(0);cin.tie(0);
// int t;cin>>t;while(t--)
while(cin>>m,m)solve();
return 0;
}
欢迎各位大佬来做题写题解,也欢迎大家踊跃在当日讨论贴中提问!
活动奖励:
在牛客博客中写出题解,并回复地址
审核通过可获得(依据题目难度和题解的内容而定)
本道题目2月4日中午12:00之前写的题解有获得牛币资格~
牛客博客开通方式
- 如何开通牛客博客:https://www.nowcoder.com/discuss/202952
- 如何使用博客搬家功能:进入博客--->设置--->底部博客搬家
- 如果你对牛客博客有任何意见或建议:牛客博客意见反馈专贴

全部评论
(3) 回帖