首页 > 其他分享 >题解 P7250 [BalticOI 2012 Day1] 山峰

题解 P7250 [BalticOI 2012 Day1] 山峰

时间:2023-07-17 21:22:13浏览次数:30  
标签:tmp fir tx ty int 题解 tot Day1 P7250

通过观察,可以发现此题和最小生成树十分相似(两个地点之间途经的最小值最大)。

于是可以考虑这么做:

  1. 通过 bfs 将每一个块预处理出来,并记录其编号、高度、类型(是否为高地)以及边缘的点。

  2. 将每一个块按高度从大到小排序。

  3. 依次枚举每个块:

    • 对于当前要处理的块,枚举其边界的所有点,看会与哪些已插入的块相连,同时用并查集找到他们的“祖先”,即该块所连接形成的连通块内最高的高地。然后除了最高的“祖先”,其他高地途经的最小值最大为当前块的高度。

    • 将这些集合合并,更新其“祖先”。

  4. 排序并输出答案。

要注意的是,由于每个集合内最高的高地可能有多个,所以需要开个数组记录一下。

具体看代码吧。

#include <bits/stdc++.h>
using namespace std;
const int N = 2000 + 5, N2 = 1e5 + 5;
int n, m, a[N][N], id[N][N], fa[N2], vis[N2], dd[N2], dx[] = {0, 0, -1, 1, -1, -1, 1, 1}, dy[] = {-1, 1, 0, 0, -1, 1, 1, -1}, num[N2], tot = 0;
struct node{int id, h, type; vector<int> v;}b[N2]; //块 
struct node2{int i, j; node2(int i = 0, int j = 0):i(i), j(j){}};
struct node3{int h, minn;}ans[N2];
bool cmp(node x, node y) {return x.h > y.h;}
bool cmp2(int x, int y) {return b[x].h > b[y].h;}
bool cmp3(node3 x, node3 y) {if (x.h != y.h) return x.h > y.h; return x.minn > y.minn;}
int ff(int x) {return (fa[x] == x)?x:fa[x] = ff(fa[x]);}
queue<node2> q;
vector<int> tmp;
void bfs(int sx, int sy) {
	q.push(node2(sx, sy)); ++tot;
	b[tot].id = tot; b[tot].h = a[sx][sy];
	id[sx][sy] = tot;
	int f1 = 1; //该连通块是否为高地 
	while (!q.empty()) {
		node2 u = q.front(); q.pop();
		int f2 = 0; //该点是否在边缘上 
		for (int k = 0; k <= 7; ++k) {
			int tx = u.i + dx[k], ty = u.j + dy[k];
			if (tx < 1 || tx > n || ty < 1 || ty > m) continue;
			if (a[tx][ty] == a[sx][sy] && !id[tx][ty]) q.push(node2(tx, ty)), id[tx][ty] = tot;
			if (a[tx][ty] != a[u.i][u.j]) f2 = 1;
			if (a[tx][ty] > a[u.i][u.j]) f1 = 0;
		}
		if (f2) b[tot].v.push_back((u.i - 1) * m + u.j); 
	}
	b[tot].type = f1;
}
int main() {
	ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
	cin >> n >> m; for (int i = 1; i <= n; ++i) for (int j = 1; j <= m; ++j) cin >> a[i][j];
	for (int i = 1; i <= n; ++i) {
		for (int j = 1; j <= m; ++j) {
			if (!id[i][j]) bfs(i, j);
		}
	}
	int maxn = 0, cnt = 0;
	sort(b + 1, b + tot + 1, cmp);
	for (int i = 1; i <= tot; ++i) {
		fa[i] = i; dd[b[i].id] = i; maxn = max(maxn, b[i].h);
		num[i] = b[i].type;
	}
	for (int i = 1; i <= tot; ++i) if (b[i].h == maxn) ans[++cnt].h = maxn, ans[cnt].minn = 0; //先将最高的高地的答案处理了 
	for (int t = 1; t <= tot; ++t) {
		vis[t] = 1; //记录每个块是否已插入 
		tmp.clear(); tmp.push_back(t); //tmp记录与当前块相邻的块 
		for (int i = 0; i < b[t].v.size(); ++i) {
			int x = (b[t].v[i] - 1) / m + 1, y = b[t].v[i] % m;
			if (!y) y = m;
			for (int k = 0; k <= 7; ++k) {
				int tx = x + dx[k], ty = y + dy[k];
				if (tx < 1 || tx > n || ty < 1 || ty > m) continue;
				int d = ff(dd[id[tx][ty]]);
				if (vis[d] && d != t) tmp.push_back(d); //记录其下标 
			}
		}
		sort(tmp.begin(), tmp.end());
		tmp.erase(unique(tmp.begin(), tmp.end()), tmp.end()); //去重 
		sort(tmp.begin(), tmp.end(), cmp2); //将其高度从大到小排序,方便后续处理 
		int fir = tmp[0], c = 0; //fir表示最高的高地 
		for (int i = 0; i < tmp.size(); ++i) {
			if (b[tmp[i]].type && num[tmp[i]]) {
				++c;
				if (c == 1) fir = tmp[i];
				else {
					if (b[tmp[i]].h == b[fir].h) { //高度一样,合并到fir 
						num[fir] += num[tmp[i]];
						num[tmp[i]] = 0;
					} else {
						for (int j = 0; j < num[tmp[i]]; ++j) ans[++cnt].h = b[tmp[i]].h, ans[cnt].minn = b[t].h; //记录答案 
						num[tmp[i]] = 0;
					}
				}
			}
		}
		for (int i = 0; i < tmp.size(); ++i) { //合并集合 
			if (tmp[i] != fir) {
				fa[tmp[i]] = fir;
			}
		}
	}
	sort(ans + 1, ans + cnt + 1, cmp3);
	cout << cnt << endl;
	for (int i = 1; i <= cnt; ++i) cout << ans[i].h << " " << ans[i].minn << endl;
	return 0;
}

标签:tmp,fir,tx,ty,int,题解,tot,Day1,P7250
From: https://www.cnblogs.com/HQJ2007/p/17561253.html

相关文章

  • 题解 AT3726 [ARC087B] FT Robot
    首先可以观察到一个非常重要的性质:对于一次前进的操作,如果前面有奇数次转向,则走上下,否则走左右。(当然如果一开始就前进就只能走右)于是我们可以将其拆成许多的“块”,并分成两类,即前进方向为左右还是上下。然后对于两个维度分别dp。\(f_{i},_{j}=f_{i-1},_{j-val}\|\f_{i-......
  • Charles抓取https请求及常见问题解决
    一、背景APP测试的时候,通常都需要通过抓包工具抓取各类请求,查看接口的入参、返回值等,用于分析定位问题。常用的抓包工具有fiddler、charles等,抓取http的请求比较简单,https的请求稍显复杂。由于更喜欢charles的页面风格,本篇文章主要介绍以下两点:1、Charles如何抓取电脑端和手机端的......
  • CF1808C Unlucky Numbers 题解
    可以证明答案是\(l\)或\(r\)的一段前缀,拼上后面全部相同的一段字符\(d\),证明方式类似数位dp。能够自由填的数字一定是相等的,这样不会影响幸运值。前面那些不能自由填写的,就是\(l\)或\(r\)的一段前缀。假如不是\(l\)或\(r\)的一段前缀,必然填写相等的更好,而这种情况已......
  • P7809 [JRKSJ R2] 01 序列 题解
    前言传送门blog思路Problem1问题一问的是最长不下降子序列的长度,在一个$01$串中的最长不下降子序列,总共有三种$000\dots$,$000\dots111\dots$和$111111\dots$。可以把找到以上三种最长不下降子序列问题变为:$$\max^r_{i=l}(\sum_{j=l}^i[a_j=0])+(\sum_{j=i+1}^......
  • P7333 [JRKSJ R1] JFCA 题解
    前言传送门blog思路首先看数据范围$10^5$,$O(n\log_2n)$可以过,自然想到二分。二分具有单调性,那么如何确保整个$a$序列按顺序排列呢?我们可以使用st表维护区间最大值,如果在这个距离中已经有了$a_i\geb_j$那么最大值一定指向的是新加入进来的那个值,否则在之前二分就......
  • P8708 [蓝桥杯 2020 省 A1] 整数小拼接 题解
    前言传送门blog思路这种选出两个数拼接在一起的题,一看就可以使用two-point,我们使用$l$和$r$分别从最大的和最小的开始搜索,进行两次。以$l$为头,$r$为尾。以$r$为头,$l$为尾。如何比较大小呢?我们可以先去做宇宙总统这道题。首先排序的$cmp$:boolcmp(strin......
  • UVA10791 最小公倍数的最小和 Minimum Sum LCM 题解
    前言长沙市一中8机房0714模拟测1。传送门blog思路本题思路,首先我们先看$\operatorname{lcm}$,明显要使得这些数的$\operatorname{lcm}=n$那么我们就需要所有的数的质因子必须包含$n$的质因子。若$1\lea,b$,则$a\timesb\gea+b$,所以我们就有了策略。将同一个质因......
  • P9451 [ZSHOI-R1] 新概念报数 题解
    目录DescriptionSolutionCodeDescription在此题中,对于一个数\(x\),若\(\texttt{popcount}(x)\geq3\)(即\(x\)在二进制下\(\texttt{1}\)的个数大于等于三时),那它是非法的,否则其为合法的。给定\(T\)个数,如果当前的数\(x\)是非法的,则输出No,Commander,否则输出第一个大于......
  • requests.exceptions.ProxyError问题解决方法
    出现这个问题是因为你系统上在使用代理,然后你的代理又是规则匹配的。https://stackoverflow.com/questions/36906985/switch-off-proxy-in-requests-library3种解决方法:headers={"User-Agent":"Mozilla/5.0(WindowsNT10.0;Win64;x64;rv:109.0)Gecko/20100101Fi......
  • CF512D Fox And Travelling 题解--zhengjun
    计数好题。首先对于每个连通块独立考虑,最后合并答案。发现点数超过1的强连通分量一定删不掉。若连通块中存在点数超过1的强连通分量tarjan缩点之后,称这些点数超过1的强连通分量为关键点;那么两关键点之间的点也不能删;于是对于剩下的点直接dp即可,由于可删的子树......