Luogu P7250 BalticOI 山峰

一道大模拟,很暴力,也很难写。建议紫或蓝,标签为模拟、广度优先搜索、并查集。

思路

首先观察到答案取决于路线上的最低点,所以我们可以把所有点的高度丢进一个桶里,从大到小枚举,尝试更新答案。这应该是个挺经典的 trick 了。

感性理解可以看作所有山都先浸在水中,然后水面逐步下降的过程。

所以我们先 BFS 一遍,找出所有是山峰的极大连通块,在里面随便找一个点标记一下。

然后接下来就是从大到小往一开始的空图中加点,每次加点的时候找到旁边八连通的点,如果已经加入图中,就连一条边,也就是把他们合并到一个连通块里。这个可以用并查集实现。

那么某个山峰的答案怎么统计?考虑把某个连通块的最高的山峰存进一个 vector 中,并且在合并的时候,做一个类似启发式合并的一个东西,把最高山峰的高度低的连通块的答案记录一下,然后将他们全部删除。如果两个连通块最高的山峰高度相同,就先不记录答案,把其中一个 vector 的山峰加到另一个 vector 里就可以了。

注意到这题有点卡空间,开 \(10^6\) 的数组还会导致消耗多余的空间,所以我们用 unordered_map 存一下就可以把空间卡过去了。

代码

#include <bits/stdc++.h>
#define fi first
#define se second
using namespace std;
typedef long long ll;
typedef pair<int,int> pi;
typedef pair<short,short> ps;
int n,m,nh;
unordered_map<int,int>h[2005],mh[2005],ans[2005];
vector<ps>d[1000010];
vector<pi>tot;
vector<ps>mg[2005][2005];
ps f[2005][2005];
bitset<2005>vis[2005],istop[2005];
int gox[]={0,0,1,1,1,-1,-1,-1};
int goy[]={1,-1,1,0,-1,1,0,-1};
void init()
{
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
{
f[i][j]={i,j};
mh[i][j]=0;
}
}
}
pi findf(pi x)
{
pi fa=f[x.fi][x.se];
if(fa.fi!=x.fi||fa.se!=x.se)f[x.fi][x.se]=findf(fa);
return f[x.fi][x.se];
}
void combine(ps x,ps y)
{
pi fx=findf(x),fy=findf(y);
if(fx!=fy)
{
int ls=mh[fx.fi][fx.se],mr=mh[fy.fi][fy.se];
if(ls>mr)
{
swap(ls,mr);
swap(fx,fy);
}
if(ls<mr)
{
for(auto tmp:mg[fx.fi][fx.se])
{
ans[tmp.fi][tmp.se]=nh;
}
}
else
{
for(auto tmp:mg[fx.fi][fx.se])
{
mg[fy.fi][fy.se].push_back(tmp);
}
}
mg[fx.fi][fx.se].clear();
mg[fx.fi][fx.se].shrink_to_fit();
mh[fx.fi][fx.se]=mh[fy.fi][fy.se];
f[fx.fi][fx.se]=fy;
}
}
bool legal(int x,int y)
{
return (x>=1&&x<=n&&y>=1&&y<=m);
}
void bfs(int sx,int sy)
{
bool flag=1;
queue<pi>q;
q.push({sx,sy});
vis[sx][sy]=1;
while(!q.empty())
{
pi u=q.front();
q.pop();
int x=u.fi,y=u.se;
for(int i=0;i<8;i++)
{
int tx=x+gox[i],ty=y+goy[i];
if(legal(tx,ty))
{
if(h[tx][ty]>h[sx][sy])flag=0;
if(vis[tx][ty]==0&&h[tx][ty]==h[sx][sy])
{
vis[tx][ty]=1;
q.push({tx,ty});
}
}
}
}
if(flag)
{
istop[sx][sy]=1;
mg[sx][sy].push_back({sx,sy});
mh[sx][sy]=h[sx][sy];
}
}
void gettop()
{
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
{
if(vis[i][j]==0)
{
bfs(i,j);
}
}
}
}
bool cmp(pi x,pi y)
{
return x>y;
}
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin>>n>>m;
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
{
cin>>h[i][j];
d[h[i][j]].push_back({i,j});
}
}
init();
gettop();
nh=1000001;
while(nh>=1)
{
for(auto tmp:d[nh])
{
int x=tmp.fi,y=tmp.se;
for(int i=0;i<8;i++)
{
int tx=x+gox[i],ty=y+goy[i];
if(legal(tx,ty))
{
if(h[tx][ty]>=nh)
{
combine({x,y},{tx,ty});
}
}
}
}
nh--;
}
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
{
if(istop[i][j]==1)
{
tot.push_back({h[i][j],ans[i][j]});
}
}
}
cout<<tot.size()<<endl;
sort(tot.begin(),tot.end(),cmp);
for(auto tmp:tot)
{
cout<<tmp.fi<<' '<<tmp.se<<endl;
}
return 0;
}

后记

写完突然发现我们不用知道某个连通块里最高的山峰到底是哪个,所以可以不用启发式合并,只要记录最高山峰的个数就好了。

Luogu P7250 BalticOI 山峰 题解 [ 蓝 ] [ 模拟 ] [ 并查集 ] [ BFS ]的更多相关文章

  1. 2019牛客暑期多校训练营(第八场)E:Explorer(LCT裸题 也可用线段树模拟并查集维护连通性)

    题意:给定N,M,然后给出M组信息(u,v,l,r),表示u到v有[l,r]范围的通行证有效.问有多少种通行证可以使得1和N连通. 思路:和bzoj魔法森林有点像,LCT维护最小生成树.  开始和队友 ...

  2. Intel Code Challenge Elimination Round (Div.1 + Div.2, combined) A B C D 水 模拟 并查集 优先队列

    A. Broken Clock time limit per test 1 second memory limit per test 256 megabytes input standard inpu ...

  3. HDU 2860 (模拟+并查集)

    Regroup Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Sub ...

  4. 【Luogu P2024&P1892】食物链&团伙(并查集拓展域)

    Luogu P1892 Luogu P2024 这两道一眼看过去很容易发现可以用并查集来做--但是当我们仔细阅读题面后,会发现其实并没有那么简单. 我们知道并查集可以很轻松地维护具有传递性的信息,也就 ...

  5. P4675 [BalticOI 2016 day1]Park (并查集)

    题面 在 Byteland 的首都,有一个以围墙包裹的矩形公园,其中以圆形表示游客和树. 公园里有四个入口,分别在四个角落( 1 , 2 , 3 , 4 1, 2, 3, 4 1,2,3,4 分别对应 ...

  6. Codeforces Round #376 (Div. 2) A B C 水 模拟 并查集

    A. Night at the Museum time limit per test 1 second memory limit per test 256 megabytes input standa ...

  7. luogu 2294 狡猾的商人 带权并查集

    此题做法多啊 带权并查集,区间dp,前缀和,差分约束 1.自己写的前缀和, 11 #include<bits/stdc++.h> #define rep(i,x,y) for(regist ...

  8. NYOJ 208 Supermarket (模拟+并查集)

    题目链接 描述 A supermarket has a set Prod of products on sale. It earns a profit px for each product x∈Pr ...

  9. bzoj1854 游戏题解(二分图/并查集)

    1854: [Scoi2010]游戏 Time Limit: 5 Sec  Memory Limit: 162 MBSubmit: 5547  Solved: 2229[Submit][Status] ...

  10. Luogu P1525 [NOIp2010提高组]关押罪犯 | 并查集

    题目链接 这一道题,我用了并查集来做.在此题中,并查集的作用就是:将同一个监狱里的罪犯合并到一起. 思路:将每对罪犯之间的怨气值从大到小排序,再依次把他们分到不同的两个监狱里,当发现这一对罪犯已经在同 ...

随机推荐

  1. arcpy获取polygon内环

    当使用arcpy获取polygon几何的时候,不能像ao一样获取到内外环,只能获取到单个部件.而part返回的即是一个点组了. 所以只能通过None对象进行分割,确定部件内的内外环.一个part内,只 ...

  2. Uniapp input的v-model问题

    前情 uni-app是我很喜欢的跨平台框架,它能开发小程序,H5,APP(安卓/iOS),对前端开发很友好,自带的IDE让开发体验也很棒,公司项目就是主推uni-app. 坑位 最近在做一个input ...

  3. C++顺序结构(1)任务

    1.下载并观看视频(照着做,多看几遍) https://www.jianguoyun.com/p/DWCNkNEQi8_wDBj5ptYFIAA 2.两项照着做的任务

  4. 腾讯云 CHDFS 助力微信秒级异常检测

    微信全景监控平台介绍 微信全景监控平台,是微信的多维指标 OLAP 监控以及数据分析平台.支持自定义多维度指标上报,海量数据实时上卷下钻分析,提供了秒级异常检测告警能力. 项目高效支撑了视频号.微信支 ...

  5. 【Java】【Spring Boot】CP01:创建一个SpringBoot项目(Spring Initializr)

    设置(可跳过这一步) 点击Apply 然后点击OK 创建项目 目前不需要勾选什么,以后根据需要勾选 创建一个名为controller的文件夹(控制层),并在文件夹中创建一个HelloControlle ...

  6. JSchException: Algorithm negotiation fail问题解决之路

    最近一个需求用到了SFTP上传功能,同事之前已经封装好了SFTP工具类,用的是JSch,本着不要重复造轮子的想法,就直接拿来用了.交代下环境,JDK为1.7,JSch版本为0.1.51.自测通过.测试 ...

  7. Netty内存池泄漏问题

    为了提升消息接收和发送性能,Netty针对ByteBuf的申请和释放采用池化技术,通过PooledByteBufAllocator可以创建基于内存池分配的ByteBuf对象,这样就避免了每次消息读写都 ...

  8. Qt编写安防视频监控系统48-视频参数

    一.前言 视频参数之前在基本参数中,后面越来越多,直接独立了出来,甚至还拆分出来了视频参数1.视频参数2,参数越来越多分组也越来越多的时候,你会发现分组名称都不够用或者不方便命名,不能直观的表示该分组 ...

  9. Qt开源作品11-屏幕录制控件

    一.前言 在平时的写作过程中,经常需要将一些操作动作和效果图截图成gif格式,使得涵盖的信息更全面更生动,有时候可以将整个操作过程和运行效果录制成MP4,但是文件体积比较大,而且很多网站不便于上传,基 ...

  10. Selenium 自动化浏览器,解决懒加载的网页获取问题

    Selenium 自动化浏览器,解决懒加载的网页获取问题.可以用于爬虫这些 在使用 Selenium WebDriver 进行自动化测试时,可以通过设置日志级别来控制输出的日志信息.在 C# 中,可以 ...