【BZOJ4242】水壶(克鲁斯卡尔重构树,BFS)
【BZOJ4242】水壶(克鲁斯卡尔重构树,BFS)
题面
BZOJ然而是权限题。
Description
JOI君所居住的IOI市以一年四季都十分炎热著称。
IOI市是一个被分成纵H*横W块区域的长方形,每个区域都是建筑物、原野、墙壁之一。建筑物的区域有P个,编号为1...P。
JOI君只能进入建筑物与原野,而且每次只能走到相邻的区域中,且不能移动到市外。
JOI君因为各种各样的事情,必须在各个建筑物之间往返。虽然建筑物中的冷气设备非常好,但原野上的日光十分强烈,因此在原野上每走过一个区域都需要1单位的水。此外,原野上没有诸如自动售货机、饮水处之类的东西,因此IOI市的市民一般都携带水壶出行。大小为x的水壶最多可以装x单位的水,建筑物里有自来水可以将水壶装满。
由于携带大水壶是一件很困难的事情,因此JOI君决定携带尽量小的水壶移动。因此,为了随时能在建筑物之间移动,请你帮他写一个程序来计算最少需要多大的水壶。
现在给出IOI市的地图和Q个询问,第i个询问(1<=i<=Q)为“在建筑物Si和Ti之间移动,最小需要多大的水壶?”,请你对于每个询问输出对应的答案。
Input
第一行四个空格分隔的整数H,W,P,Q,表示IOI市被分成了纵H*横W块区域,有P个建筑物,Q次询问。
接下来H行,第i行(1<=i<=H)有一个长度为W的字符串,每个字符都是’.’或’#’之一,’.’表示这个位置是建筑物或原野,’#’表示这个位置是墙壁。
接下来P行描述IOI市每个建筑物的位置,第i行(1<=i<=P)有两个空格分隔的整数Ai和Bi,表示第i个建筑物的位置在第Ai行第Bi列。保证这个位置在地图中是’.’
接下来Q行,第i行(1<=i<=Q)有两个空格分隔的整数Si和Ti,表示第i个询问为“在建筑物Si和Ti之间移动,最小需要多大的水壶?”
Output
输出Q行,第i行(1<=i<=Q)一个整数,表示在建筑物Si和Ti之间移动最小需要多大的水壶。
如果无法到达,输出-1。此外,如果不需要经过原野就能到达,输出0。
Sample Input
5 5 4 4
.....
..##.
.#...
..#..
.....
1 1
4 2
3 3
2 5
1 2
2 4
1 3
3 4
Sample Output
3
4
4
2
HINT
1<=H<=2000
1<=W<=2000
2<=P<=2*10^5
1<=Q<=2*10^5
1<=Ai<=H(1<=i<=P)
1<=Bi<=W(1<=i<=P)
(Ai,Bi)≠(Aj,Bj)(1<=i<j<=P)
1<=Si<Ti<=P(1<=i<=Q)
题解
好神仙的一道题目啊。
很明显的一点,这道题目就是要在网格图上面计算一个最小生成树,然后就变成了货车运输一样的做法,可以倍增或者是克鲁斯卡尔重构树。
后面的部分就是原题,所以不再考虑,只考虑怎么样构造网格图最小生成树。
我们对于每一个可行的起点一起\(bfs\),对于每一个格子记录两个东西:一个记录距离当前位置最近的建筑的距离,以及是哪一个建筑。
当你\(bfs\)到某个格子的时候,发现这个格子已经被其他建筑给标记过了,那么就可以直接从距离当前这个格子最近的建筑连向距离拓展出来的格子最近的那个建筑。
然而这题卡常,边数有\(4*2000*2000\),所以开一个\(vector\)记录所有可能的距离,然后把所有的距离全部挂在上面跑。
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<vector>
#include<queue>
using namespace std;
#define ll long long
#define pi pair<int,int>
#define mp make_pair
#define fr first
#define sd second
#define MAX 2020
#define pb push_back
#define N 200200
inline int read()
{
int x=0;bool t=false;char ch=getchar();
while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
if(ch=='-')t=true,ch=getchar();
while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
return t?-x:x;
}
int H,W,P,Q;
pi p[N];
vector<pi> E[MAX*MAX];
char g[MAX][MAX];
int dis[MAX][MAX],bel[MAX][MAX];
int d[4][2]={1,0,0,1,-1,0,0,-1};
struct Line{int v,next;}e[N<<2];
int h[N<<1],cnt=1;
inline void Add(int u,int v){e[cnt]=(Line){v,h[u]};h[u]=cnt++;}
void bfs()
{
queue<pi> Q;
for(int i=1;i<=P;++i)Q.push(p[i]),bel[p[i].fr][p[i].sd]=i;
while(!Q.empty())
{
pi u=Q.front();Q.pop();
for(int i=0;i<4;++i)
{
pi v=mp(u.fr+d[i][0],u.sd+d[i][1]);
if(g[v.fr][v.sd]=='#'||v.fr<1||v.fr>H||v.sd<1||v.sd>W)continue;
if(!bel[v.fr][v.sd])
{
Q.push(v);
bel[v.fr][v.sd]=bel[u.fr][u.sd];
dis[v.fr][v.sd]=dis[u.fr][u.sd]+1;
}
else E[dis[v.fr][v.sd]+dis[u.fr][u.sd]].pb(mp(bel[v.fr][v.sd],bel[u.fr][u.sd]));
}
}
}
int f[N<<1];
int getf(int x){return x==f[x]?x:f[x]=getf(f[x]);}
int CNT,w[N<<1],dep[N<<1];
int fa[22][N<<1];
void dfs(int u)
{
dep[u]=dep[fa[0][u]]+1;
for(int i=h[u];i;i=e[i].next)
dfs(e[i].v);
}
void Kruskal()
{
for(int i=1;i<=P;++i)f[i]=i;CNT=P;
for(int i=0;i<=H*W;++i)
for(int j=0,l=E[i].size();j<l;++j)
{
int u=getf(E[i][j].fr),v=getf(E[i][j].sd);
if(u==v)continue;++CNT;
f[CNT]=f[u]=f[v]=CNT;w[CNT]=i;
Add(fa[0][u]=CNT,u);Add(fa[0][v]=CNT,v);
}
for(int i=1;i<=21;++i)
for(int j=1;j<=CNT;++j)
fa[i][j]=fa[i-1][fa[i-1][j]];
}
int LCA(int u,int v)
{
if(dep[u]<dep[v])swap(u,v);
for(int i=21;~i;--i)
if(dep[fa[i][u]]>=dep[v])u=fa[i][u];
if(u==v)return u;
for(int i=21;~i;--i)
if(fa[i][u]!=fa[i][v])
u=fa[i][u],v=fa[i][v];
return fa[0][u];
}
int main()
{
H=read();W=read();P=read();Q=read();
for(int i=1;i<=H;++i)scanf("%s",g[i]+1);
for(int i=1;i<=P;++i)p[i].fr=read(),p[i].sd=read();
bfs();Kruskal();for(int i=CNT;i;--i)if(!dep[i])dfs(i);
int cnt=0;
while(Q--)
{
++cnt;
int u=read(),v=read();
if(getf(u)!=getf(v))puts("-1");
else printf("%d\n",w[LCA(u,v)]);
}
return 0;
}
【BZOJ4242】水壶(克鲁斯卡尔重构树,BFS)的更多相关文章
- [您有新的未分配科技点][BZOJ3545&BZOJ3551]克鲁斯卡尔重构树
这次我们来搞一个很新奇的知识点:克鲁斯卡尔重构树.它也是一种图,是克鲁斯卡尔算法求最小生成树的升级版首先看下面一个问题:BZOJ3545 Peaks. 在Bytemountains有N座山峰,每座山峰 ...
- 洛谷P4197 Peaks&&克鲁斯卡尔重构树学习笔记(克鲁斯卡尔重构树+主席树)
传送门 据说离线做法是主席树上树+启发式合并(然而我并不会) 据说bzoj上有强制在线版本只能用克鲁斯卡尔重构树,那就好好讲一下好了 这里先感谢LadyLex大佬的博客->这里 克鲁斯卡尔重构树 ...
- 洛谷 P1967 货车运输(克鲁斯卡尔重构树)
题目描述 AAA国有nn n座城市,编号从 11 1到n nn,城市之间有 mmm 条双向道路.每一条道路对车辆都有重量限制,简称限重.现在有 qqq 辆货车在运输货物, 司机们想知道每辆车在不超过车 ...
- 【BZOJ5415】【NOI2018】归程(克鲁斯卡尔重构树)
[NOI2018]归程(克鲁斯卡尔重构树) 题面 洛谷 题解 我在现场竟然没有把这道傻逼题给切掉,身败名裂. 因为这题就是克鲁斯卡尔重构树的模板题啊 我就直接简单的说一下把 首先发现答案就是在只经过海 ...
- [note]克鲁斯卡尔重构树
克鲁斯卡尔重构树 又叫并查集重构树 大概在NOI2018之前还是黑科技 现在?烂大街了 主要是针对图上的对边有限制的一类问题 比如每次询问一个点u不能经过边权大于w的边能走到的第k大点权是多少 也就是 ...
- 洛谷P4768 [NOI2018]归程(克鲁斯卡尔重构树+最短路)
传送门 前置技能,克鲁斯卡尔重构树 我们按道路的高度建一个最大生成树,然后建好克鲁斯卡尔重构树 那么我们需要知道一颗子树内到1点距离最近是多少(除此之外到子树内任何一个点都不需要代价) 可以一开始直接 ...
- [模板] Kruskal算法 && 克鲁斯卡尔重构树
克鲁斯卡尔重构树 发现没把板子放上来... 现在放一下 克鲁斯卡尔算法的正确性是利用反证法证明的. 简要地说, 就是如果不加入当前权值最小的边 \(e_1\), 那么之后加入的边和这条边会形成一个环. ...
- P4197 Peaks [克鲁斯卡尔重构树 + 主席树][克鲁斯卡尔重构树学习笔记]
Problem 在\(Bytemountains\)有\(n\)座山峰,每座山峰有他的高度\(h_i\) .有些山峰之间有双向道路相连,共\(M\)条路径,每条路径有一个困难值,这个值越大表示越难走, ...
- P5168 xtq玩魔塔 [克鲁斯卡尔重构树+带修莫队]
P5168 xtq玩魔塔 又是码农题- 利用克鲁斯卡尔重构树的性质 我们就可以得出 \(dep\) 值小的,肯定比 \(dep\) 大的值要优. 于是第二问就可以直接 LCA 求出来了- 至于第三问, ...
随机推荐
- 错误结果保存示例 - 【jmeter】
- 2018NOIP爆0记第一弹
初赛篇 选择即王道 迪杰斯特拉那道题的A选项自己yy一下觉得甚是不妥,就没选 就和30分完美选择题擦肩而过. 填空最后一题不太会搞,就跳过了,最后蒙了个512上去...其实还有点接近的... 5分 然 ...
- js多条件if语句简写发生Uncaught SyntaxError: Unexpected token }
改写原生js 多条件if判断语句时,采用三元方法,发生Uncaught SyntaxError: Unexpected token } function compareImgSize() { var ...
- SQL中读取Excel 以及 bpc语言
--开启导入功能 reconfigure reconfigure --允许在进程中使用ACE.OLEDB.12 --允许动态参数 EXEC master.dbo.sp_MSset_oledb_prop ...
- 转载---VisualStudioCode通过SSH远程编辑文件
最近需要长期修改远端服务器上的代码,调试.vim操作又不是很6,想到了远程操作的办法,找到一篇好用的bolg,记录一下. 原文链接:https://blog.csdn.net/qq_38401919/ ...
- DataGridView 复选框 操作大全
DataGridViewCheckBoxColumn dtCheck = new DataGridViewCheckBoxColumn(); dtCheck.DataPropertyName = &q ...
- 部署mysql版本项目问题记录
一,com.mysql.cj.jdbc.exceptions.CommunicationsException: Communications link failure报错 将url从jdbc:mysq ...
- No.110_第三次团队会议
前端的易帜 前端在整个软件中有着举足轻重的地位.前端设计一般可以理解为视觉设计,前端开发则是前台代码的实现. 随着科技水平的提高和生产力的提高,人民对于审美的要求逐渐增高.在没有科技壁垒的情况下,是否 ...
- Linux环境下Web环境搭建——Nginx
1.安装依赖 yum -y install make gcc gcc-c++ ncurses-devel ##编译环境 yum -y install zlib zlib-devel openssl ...
- delphi 图像处理 图像左旋右旋
procedure TDR_QM_ZP_Form.btn_ZXClick(Sender: TObject); //图像左旋 begin screen.Cursor := crhourglass; my ...