题解:P12169 [蓝桥杯 2025 省 C/Python A/Java C] 登山
算法:并查集
由于行列是同一套逻辑,所以下面只说同一列内的行操作,同一行内的列操作直接照抄即可。
设现在我们在
那么同理,如果我们在
也就是说,只要存在一个逆序对,就有一对双向边!
但是直接
先给出命题:对于第
为了方便,做几点说明。
- 逆序对连边生成的边集为
E_0 ,相邻连边生成的边集为E_1 ; - 简写
(l, j) 到(r, j) 连双向边为l \Longleftrightarrow r 。
下面证明二者等价。
若有
若有
综上,
设我们这样得到了
时间复杂度 O(\rm{nm \cdot \alpha(nm)})
- 其中
\rm{\alpha(nm)} 是反阿克曼函数,一般可以看作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;
}