当前位置: 首页 > news >正文

【第十六届 蓝桥杯 省 C/Python A/Java C 登山】题解

题目链接:P12169 [蓝桥杯 2025 省 C/Python A/Java C] 登山

思路来源

一开始想的其实是记搜,但是发现还有先找更小的再找更大的这种路径,所以这样可能错过某些最优决策,这样不行。

于是我又想能不能从最大值出发往回搜,手玩了一下发现其实和记搜没什么区别,无非是把边给反向了。

那可能的做法就是强连通分量?我当时板子都掏出来了,但是模拟了一番之后就发现可以用并查集。

下面是正文。

算法:并查集

由于行列是同一套逻辑,所以下面只说同一列内的行操作,同一行内的列操作直接照抄即可。

设现在我们在 ( x , y ) (x, y) (x,y),且存在点 ( i , y ) (i, y) (i,y) 满足 i > x i > x i>x a i , y < a x , y a_{i, y} < a_{x, y} ai,y<ax,y,那么我们可以从 ( x , y ) (x, y) (x,y) ( i , y ) (i, y) (i,y) 连边表示 ( x , y ) (x, y) (x,y) 可达 ( i , y ) (i, y) (i,y)

那么同理,如果我们在 ( i , y ) (i, y) (i,y),根据题中所说, ( x , y ) (x, y) (x,y) 满足 x < i x < i x<i a x , y > a i , y a_{x, y} > a_{i, y} ax,y>ai,y,同样可以从 ( i , y ) (i, y) (i,y) ( x , y ) (x, y) (x,y) 连边。

也就是说,只要存在一个逆序对,就有一对 双向边

但是直接 O ( n 2 ) O(n^2) O(n2) 遍历 O ( m ) O(m) O(m) 次来连边有点太狂野了,这复杂度也过不去,所以我们开始寻找逆序对连边的等价命题。

先给出命题:

对于第 j j j 列,设 p r e [ i ] pre[i] pre[i] 表示前 i i i 个元素的最大值, s u f [ i ] suf[i] suf[i] 表示 [ i , n ] [i, n] [i,n] 内元素的最小值,如果 p r e [ i ] > s u f [ i + 1 ] pre[i] > suf[i + 1] pre[i]>suf[i+1],就连一条 ( i , j ) (i, j) (i,j) ( i + 1 , j ) (i + 1, j) (i+1,j) 的边。

为了方便,做几点说明。

  • 逆序对连边生成的边集为 E 0 E_0 E0,相邻连边生成的边集为 E 1 E_1 E1
  • 简写 ( l , j ) (l, j) (l,j) ( r , j ) (r, j) (r,j) 连双向边为 l ⟺ r l \Longleftrightarrow r lr

下面证明二者等价。

若有 a l , j > a r , j a_{l, j} > a_{r, j} al,j>ar,j,那么有 l ⟺ r l \Longleftrightarrow r lr,那么对于 i ∈ [ l , r ) i \in [l, r) i[l,r),一定有 p r e [ i ] > s u f [ i + 1 ] pre[i] > suf[i + 1] pre[i]>suf[i+1],那也就是说, E 1 E_1 E1 会包含 l ⟺ l + 1 , l + 1 ⟺ l + 2 , ⋯ , r − 1 ⟺ r l \Longleftrightarrow l + 1, \ \ l + 1 \Longleftrightarrow l + 2, \ \ \cdots, \ \ r - 1 \Longleftrightarrow r ll+1,  l+1l+2,  ,  r1r,这相当于包含了一条 l ⟺ r l \Longleftrightarrow r lr 的双向边,所以 E 0 ⊆ E 1 E_0 \subseteq E_1 E0E1

若有 p r e [ i ] > s u f [ i + 1 ] pre[i] > suf[i + 1] pre[i]>suf[i+1],那么有 i ⟺ i + 1 i \Longleftrightarrow i + 1 ii+1,同时,由前缀最大和后缀最小的性质可以得到,必然存在 l ∈ [ 1 , i ] l \in [1, i] l[1,i] r ∈ ( i , n ] r \in (i, n] r(i,n],使得 a l , j > a r , j a_{l, j} > a_{r, j} al,j>ar,j,那么首先就有 l ⟺ r l \Longleftrightarrow r lr。如果 l < i l < i l<i,说明 a l , j > a i , j a_{l, j} > a_{i, j} al,j>ai,j,那么由题目条件有 l ⟺ i l \Longleftrightarrow i li,同理如果 i + 1 < r i + 1 < r i+1<r,那么 i + 1 ⟺ r i + 1 \Longleftrightarrow r i+1r,将这三条边合起来,就包含了一条 i ⟺ i + 1 i \Longleftrightarrow i + 1 ii+1 的双向边,所以 E 1 ⊆ E 0 E_1 \subseteq E_0 E1E0

综上, E 0 = E 1 E_0 = E_1 E0=E1,二者等价。

设我们这样得到了 N N N 个连通块,第 i i i 个连通块的大小为 s i z i siz_i sizi,其块内最大值为 max ⁡ i \max_i maxi,则最终答案为

∑ i = 1 N s i z i × m a x i . \sum\limits_{i = 1}^{N}siz_i \times max_i. i=1Nsizi×maxi.

时间复杂度 O ( n m ⋅ α ( n m ) ) O(\rm{nm \cdot \alpha(nm)}) O(nmα(nm))
  • 其中 α ( n m ) \rm{\alpha(nm)} α(nm) 是反阿克曼函数,一般可以看作 3 , 4 3, 4 3,4 左右的常数。
C++ Code
#include <bits/stdc++.h>using i64 = long long;struct DSU {std::vector<int> f, sz;DSU() {}DSU(int n) {init(n);}void init(int n) {f.resize(n);std::iota(f.begin(), f.end(), 0);sz.assign(n, 1);}int find(int x) {while (x != f[x]) {x = f[x] = f[f[x]];}return x;}int size(int x) {return sz[find(x)];}bool merge(int x, int y) {x = find(x);y = find(y);if (x == y) {return false;}sz[x] += sz[y];f[y] = x;return true;}bool same(int x, int y) {return find(x) == find(y);}
};template<class T>
void chmax(T &a, T b) {if (a < b) {a = b;}
}
constexpr int inf = std::numeric_limits<int>::max() / 2;int main() {std::ios::sync_with_stdio(false);std::cin.tie(nullptr);std::cout << std::fixed << std::setprecision(6);int n, m;std::cin >> n >> m;const int N = n * m;std::vector<int> a(N);for (int i = 0; i < n; i++) {for (int j = 0; j < m; j++) {std::cin >> a[i * m + j];}}DSU dsu(N);for (int i = 0; i < n; i++) {std::vector pre(m + 1, 0);std::vector suf(m + 1, inf);for (int j = 0; j < m; j++) {pre[j + 1] = std::max(pre[j], a[i * m + j]);}for (int j = m - 1; j >= 0; j--) {suf[j] = std::min(suf[j + 1], a[i * m + j]);}for (int j = 1; j < m; j++) {if (pre[j] > suf[j]) {dsu.merge(i * m + (j - 1), i * m + j);}}}for (int j = 0; j < m; j++) {std::vector pre(n + 1, 0);std::vector suf(n + 1, inf);for (int i = 0; i < n; i++) {pre[i + 1] = std::max(pre[i], a[i * m + j]);}for (int i = n - 1; i >= 0; i--) {suf[i] = std::min(suf[i + 1], a[i * m + j]);}for (int i = 1; i < n; i++) {if (pre[i] > suf[i]) {dsu.merge((i - 1) * m + j, i * m + j);}}}std::vector max(N, 0);for (int i = 0; i < N; i++) {chmax(max[dsu.find(i)], a[i]);}i64 ans = 0;for (int i = 0; i < N; i++) {if (dsu.find(i) == i) {ans += 1LL * dsu.size(i) * max[i];}}std::cout << 1.* ans / n / m << "\n";return 0;
}

相关文章:

  • <数据集>小船识别数据集<目标检测>
  • 平板电脑做欧盟网络安全法案(EU)2022/30
  • 14.第二阶段x64游戏实战-分析人物的名字
  • 基于opencv和PaddleOCR识别身份证信息
  • Spring Boot 整合 JavaFX 核心知识点详解
  • 科学视角下的养生新范式——高压氧舱:重塑健康边界的氧护革命
  • 使用 Electron 打包可执行文件和资源:完整实战教程
  • Prompt 攻击与防范:大语言模型安全的新挑战
  • 文字、语音、图片、视频四个模态两两之间(共16种转换方向)的生成技术及理论基础的详细说明及表格总结
  • 【2025面试Java常问八股之redis】zset数据结构的实现,跳表和B+树的对比
  • 基于大模型的血栓性外痔全流程风险预测与治疗管理研究报告
  • Linux系统下docker 安装 redis
  • hadoop与spark的区别和联系
  • 蚂蚁全媒体总编刘鑫炜再添新职,出任共工新闻社新媒体研究院院长
  • n8n 中文系列教程_05.如何在本机部署/安装 n8n(详细图文教程)
  • Java 服务器端 jar 包内 class 文件替换与配置文件修改高级技术指南
  • 在 Spring Boot 项目中怎么识别和优化慢 SQL ?
  • 商场app测试项目
  • Unity使用Rider的常用快捷键
  • win11修改文件后缀名
  • 牛市早报|现货黄金价格站上3400美元,上交所召开私募机构座谈会
  • 高架上2名儿童从轿车天窗探出身来,驾驶员被记3分罚200元
  • “小时光:地铁里的阅读”摄影展开幕,嘉宾共话日常生活与阅读
  • 电子产品已拆封,还能申请“七天无理由退货”吗?上海法院这样判
  • 广西出现今年首场超警洪水
  • 上海群文创作大检阅,102个节目角逐群星奖