P3225 [HNOI2012]矿场搭建[割点]
题目描述
煤矿工地可以看成是由隧道连接挖煤点组成的无向图。为安全起见,希望在工地发生事故时所有挖煤点的工人都能有一条出路逃到救援出口处。于是矿主决定在某些挖煤点设立救援出口,使得无论哪一个挖煤点坍塌之后,其他挖煤点的工人都有一条道路通向救援出口。
请写一个程序,用来计算至少需要设置几个救援出口,以及不同最少救援出口的设置方案总数。
解析
这道题不错,很考验思考问题的全面性,而且代码写起来似乎也比较坑。
咱写挂了(哭)。
乍一看似乎第一问就是要我们求连通块个数,第二问就是要我们求每个连通块中点的个数。
对,但这只是一小部分,我们少考虑了一些东西。
这道题需要分类讨论。
- 如果某个连通块一个割点都没有会怎么样?那么在这个子图中,我们在任意两个位置设置出口即可,对于该连通块,若其包含\(x\)个点,其方案数为\(x(x-1)/2\)。
- 如果某个连通块只有一个割点,我们在此连通块的任意一点都可以放置出口,并统计方案数。
- 特别特别需要注意的一个坑点,我们在考虑设置出口时,要知道此题只会出现一个坍塌的挖煤点!所以,如果某个连通块有两个及以上个割点,其还是可以与其它连通块相连,所以这个连通块中是不必设置出口的!
这题最核心的一点就是要注意到只会出现一个坍塌点,所以任何时候我们都只用考虑只有一个割点时的情况。
提一些细节,在dfs划分连通块时,我们不能只记某个点是否到达过,我们得记它属于哪个连通块,否则会少算一些或者是算重复一些(这个还要看你怎么写)。
为什么要提呢,因为我写错了,我甚至还没有注意到只会出现一个坍塌点。
参考代码
#include<cstdio>
#include<iostream>
#include<cmath>
#include<cstring>
#include<ctime>
#include<cstdlib>
#include<algorithm>
#include<queue>
#include<set>
#include<map>
#define ll long long
#define N 501
using namespace std;
inline ll read()
{
ll f=1,x=0;char c=getchar();
while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();}
return x*f;
}
struct rec{
int next,ver;
}g[N*N];
ll head[N],tot,n,m,low[N],dfn[N],cnt,root,c[N],num,k,now;
ll ans1,ans2;
bool cut[N];
ll v[N];
inline void add(int x,int y)
{
g[++tot].ver=y;
g[tot].next=head[x],head[x]=tot;
}
inline void tarjan(int x)//tarjan求割点板子
{
low[x]=dfn[x]=++cnt;
int flag=0;
for(int i=head[x];i;i=g[i].next){
int y=g[i].ver;
if(!dfn[y]){
tarjan(y);
low[x]=min(low[x],low[y]);
if(low[y]>=dfn[x]){
flag++;
if(x!=root||flag>1) cut[x]=1;
}
}
else low[x]=min(low[x],dfn[y]);
}
}
inline void dfs(int x)
{
v[x]=num;++k;//划分连通块num,统计该连通块中点的个数k
for(int i=head[x];i;i=g[i].next){
int y=g[i].ver;
if(v[y]!=num&&cut[y]) v[y]=num,now++;//这里卡了我许久,这样写才能避免重复计算该连通块包含割点数量
if(!v[y]) dfs(y);
}
}
int main()
{
int kase=0;
while(~scanf("%d",&n)&&n){
memset(head,0,sizeof(head));
memset(cut,0,sizeof(cut));
memset(low,0,sizeof(low));
memset(dfn,0,sizeof(dfn));
memset(v,0,sizeof(v));
tot=ans2=1;ans1=m=0;
for(int i=1;i<=n;++i){
ll u,v;
u=read(),v=read();
add(u,v),add(v,u);
m=max(m,max(v,u));
}
cnt=num=0;
for(int i=1;i<=m;++i)
if(!dfn[i]) root=i,tarjan(i);
for(int i=1;i<=m;++i)
if(!v[i]&&!cut[i]){
k=now=0,num++;
dfs(i);
if(now==0){//注意,这里是重点
ans1+=2;
ans2*=(k-1)*k/2;
}
if(now==1) ans1++,ans2*=k;
}
printf("Case %d: %lld %lld\n",++kase,ans1,ans2);
}
return 0;
}
P3225 [HNOI2012]矿场搭建[割点]的更多相关文章
- P3225 [HNOI2012]矿场搭建 割点 tarjan 双联通分量
https://www.luogu.org/problemnew/show/P3225 题意 煤矿工地可以看成是由隧道连接挖煤点组成的无向图.为安全起见,希望在工地发生事故时所有挖煤点的工人都能有一条 ...
- 洛谷 P3225 [HNOI2012]矿场搭建 解题报告
P3225 [HNOI2012]矿场搭建 题目描述 煤矿工地可以看成是由隧道连接挖煤点组成的无向图.为安全起见,希望在工地发生事故时所有挖煤点的工人都能有一条出路逃到救援出口处.于是矿主决定在某些挖煤 ...
- 洛谷——P3225 [HNOI2012]矿场搭建
P3225 [HNOI2012]矿场搭建 题目描述 煤矿工地可以看成是由隧道连接挖煤点组成的无向图.为安全起见,希望在工地发生事故时所有挖煤点的工人都能有一条出路逃到救援出口处.于是矿主决定在某些挖煤 ...
- P3225 [HNOI2012]矿场搭建 tarjan割点
这个题需要发现一点规律,就是先按割点求块,然后求每个联通块中有几个割点,假如没有割点,则需要建两个出口,如果一个割点,则需要建一个出口,2个以上不用建. 题干: 题目描述 煤矿工地可以看成是由隧道连接 ...
- P3225 [HNOI2012]矿场搭建 题解
这道题挺难的,可以加深对割点的理解,还有,排列组合好重要了,分连通块,然后乘法原理(加法原理计数什么的) 传送门 https://www.luogu.org/problem/P3225 省选oi题 ...
- 洛谷P3225 HNOI2012 矿场搭建
题目描述 煤矿工地可以看成是由隧道连接挖煤点组成的无向图.为安全起见,希望在工地发生事故时所有挖煤点的工人都能有一条出路逃到救援出口处.于是矿主决定在某些挖煤点设立救援出口,使得无论哪一个挖煤点坍塌之 ...
- 洛谷 P3225 [HNOI2012]矿场搭建
传送门 题目大意:建设几个出口,使得图上无论哪个点被破坏,都可以与出口联通. 题解:tarjian求割点 首先出口不能建在割点上,找出割点,图就被分成了几个联通块. 每个联通块,建出口.如果割点数为0 ...
- P3225 [HNOI2012]矿场搭建
传送门 对于一个点双联通分量,如果它连接了两个或更多割点 那么不论哪个点GG都有至少一条路通到其他的点双联通分量,所以我们不用考虑 如果它只连接一个割点,如果这个割点GG,那整个块也一起GG,所以要再 ...
- [Luogu] P3225 [HNOI2012]矿场搭建
题目描述 煤矿工地可以看成是由隧道连接挖煤点组成的无向图.为安全起见,希望在工地发生事故时所有挖煤点的工人都能有一条出路逃到救援出口处.于是矿主决定在某些挖煤点设立救援出口,使得无论哪一个挖煤点坍塌之 ...
随机推荐
- 安装hbase分布式集群出现的报错- ERROR:org.apache.hadoop.hbase.PleaseHoldException: Master is initializing
可能的原因如下: 1. 时间没有同步 HBase需要结点间的时间必须是同步的,可以使用date命令在Linux查看时间(同步时间命令:ntpdate 1.cn.pool.ntp.org) 2. 底层采 ...
- .Net Core使用 MiniProfiler 进行性能分析
官方文档: https://miniprofiler.com/dotnet/AspDotNetCore 1.添加包 MiniProfiler.AspNetCore.Mvc 和 MiniPro ...
- 博客迁移到github了
博客迁移到github了,这边基本不更新, 主要是没有找到快捷的同步方法,手动同步太麻烦了,如果你有快速把github博客同步到博客园的方法请一定告诉我
- eslint 验证vue文件 报错 unexpected token =解决方法
解决方案:.eslintrc更改文件配置 { "extends": [ 'standard' ], "parserOptions": { "parse ...
- linux初识1
linux 操作系统 概念性的理解 1.Linux内置解释器bash 相当于pyhon解释器 2.Linux的内部大多是使用python去书写 云计算 1.只需要 花钱,买腾讯,阿里云服务器 2.专人 ...
- 27 多线程(一)——创建进程的三种方法、线程锁(同步synchornized与lock)
线程的流程 线程的创建 有三种方法,重点掌握前两种: 继承Thread类 实现Runnable接口(推荐使用:避免单继承的局限性) 实现Callable接口 根据java的思想,要少用继承,多用实现. ...
- docker深入学习三
docker学习三:network docker支持容器之间的网络通信,docker的网络通信方式有以下五种: bridge docker 默认的网络驱动,如果不指定网络驱动,docker就会创建一个 ...
- 基础数字电路的Verilog写法
Verilog是硬件描述电路,我对此一直稀里糊涂,于是将锆石科技开发板附带的的一些基础数字电路Verilog程序整理记录下来,并且查看他们的RTL视图,总算有点理解了. 1.基本运算符 module ...
- Huawei重新开启隐藏桌面功能
在HUAWEI的EMUI系统7.0的时候我们都能发现桌面上靠手指操作的隐藏桌面的功能,像这样: 但是在之后的EMUI8.0.9.0,之后就没有办法用了.后来问了官方,这个功能确实是被去掉了.个人也很不 ...
- QT支持TTF字体
1.编译QT库时需要支持TTF字体 ./configure -qt-freetype -fontconfig ... 2.将TTF字符库拷贝到QT_QPA_FONTDIR下 # 注意,如果不清楚字体所 ...