首页 > 其他分享 >Luogu P7250 BalticOI 山峰 题解 [ 蓝 ] [ 模拟 ] [ 并查集 ] [ BFS ]

Luogu P7250 BalticOI 山峰 题解 [ 蓝 ] [ 模拟 ] [ 并查集 ] [ BFS ]

时间:2024-08-26 21:47:49浏览次数:11  
标签:nh 山峰 sy sx int 题解 查集 Luogu 2005

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;
}

后记

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

标签:nh,山峰,sy,sx,int,题解,查集,Luogu,2005
From: https://www.cnblogs.com/zhr0102/p/18381659

相关文章

  • 题解:P9256 [PA 2022] Muzyka pop 2
    题解:P9256[PA2022]Muzykapop2题目传送门题目重点从前往后比较,和数字比较一样,如:12345<12445。如果一个串是另一个串的前缀,那么不是前缀串的那个字典序小。题目思路我爱贪心贪心就行了,每次让x增加1,找出1的个数来实现。要求序列是字典序最小的,因此每次选择尽可......
  • 动态dp——P8820 [CSP-S 2022] 数据传输 题解
    P8820[CSP-S2022]数据传输可怜的cnblog被(昨天DDos+今天CC)攻击了(望周知!),只好先发在CSDN题面:题目描述小C正在设计计算机网络中的路由系统。测试用的网络总共有nn......
  • 【跨域问题解决】Access to XMLHttpRequest at xxx from origin xxx has been blocked
    这个错误是由于浏览器的同源策略(CORS,Cross-OriginResourceSharing)导致的。当从一个源(origin)向另一个源请求资源时,如果这两个源的协议、域名或端口号不同,就会触发CORS策略。解决方法要解决这个问题,你需要在你的后端服务中添加CORS支持,以便它允许来自你的请求。这通常......
  • [ARC182C] Sum of Number of Divisors of Product 题解
    题目链接点击打开链接题目解法我怎么不会分治/fn首先把\(x\)分解成\(\prodp_i^{x_i}(0\lei\le5)\)的形式,正因数个数为\(\prod(x_i+1)\)有一个很牛的想法是:合并两个\(x_i\)序列(假设一个叫\(x_0,...,x_5\),另一个叫\(y_0,...,y_5\))先不考虑后面的\(+1\)(可以最后......
  • 题解:AT_arc183_b [ARC183B] Near Assignment
    题意:给你长度为\(N\)的整数序列\(A,B\)以及整数\(K\)。你可以执行下列操作零次或多次。选择整数\(i\)和\(j\)(\(1\leqi,j\leqN\))。这里,\(|i-j|\leqK\)必须保持不变。然后,将\(A_{i}\)的值改为\(A_{j}\)。判断是否有可能使\(A\)与\(B\)相同。分......
  • 常见问题解决 --- 如何给一个不支持配置代理的程序抓取https流量数据
    比如我有一个C#编写票务系统,它内嵌浏览器功能,我想抓取它的流量,但是这个客户端不支持配置代理设置解决办法:1.安装配置proxifier开启全局代理服务。安装好后网上有激活码激活一下,点击profile-proxyserver,添加一个代理服务器127.0.0.1,端口8080,协议https。点击profile-prox......
  • Java | Leetcode Java题解之第374题猜数字大小
    题目:题解:publicclassSolutionextendsGuessGame{publicintguessNumber(intn){intleft=1,right=n;while(left<right){//循环直至区间左右端点相同intmid=left+(right-left)/2;//防止计算时溢出......
  • Hitachi Vantara Programming Contest 2024(AtCoder Beginner Contest 368)题解A~D
    A-Cut题意:将数组的后k个字符移到前面思路:可以用rotate()函数让数组中的元素滚动旋转rotate(v.begin(),v.begin()+n-k,v.end());直接输出后k个元素,再输出前n-k个元素for(inti=n-k;i<n;i++)write(v[i]);for(inti=0;i<n-k;i++)write(v[i]);B-Decrease2......
  • 题解:AT_joisc2017_f 鉄道旅行 (Railway Trip)
    题意鉄道旅行(RailwayTrip)分析非常神仙的倍增做法。我们设\(l_{i,j}\)表示从\(i\)点出发,停靠\(2^j\)站后能抵达的最左位置。同理设\(r_{i,j}\)表示从\(i\)点出发,停靠\(2^j\)站后能抵达的最右位置。考虑如何更新这两个状态。因为可以走回头路,所以简单的\(l......
  • 题解:SP3109 STRLCP - Longest Common Prefix
    三倍经验:UVA11996JewelMagicP4036[JSOI2008]火星人题意维护一个字符串\(S\),支持以下操作:\(Q\i\j\):输出\(\operatorname{LCP}(S[i\dotsl],S[j\dotsl])\)\(R\i\char\):用\(char\)替换\(S\)的第\(i\)个字符\(I\i\char\):在\(S\)的第\(i\)......