1 条题解

  • 0
    @ 2025-8-24 23:13:21

    自动搬运

    查看原文

    来自洛谷,原作者为

    avatar Levisuper
    **

    搬运于2025-08-24 23:13:21,当前版本为作者最后更新于2025-04-22 00:12:52,作者可能在搬运后再次修改,您可在原文处查看最新版

    自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多

    以下是正文


    算法:并查集

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

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

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

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

    但是直接 O(n2)O(n^2) 遍历 O(m)O(m) 次复杂度过高,所以我们需要逆序对连边的等价命题。

    先给出命题:对于第 jj 列,设 pre[i]pre[i] 表示前 ii 个元素的最大值,suf[i]suf[i] 表示 [i,n][i, n] 内元素的最小值,如果 pre[i]>suf[i+1]pre[i] > suf[i + 1],就连一条 (i,j)(i, j)(i+1,j)(i + 1, j) 的边。

    为了方便,做几点说明。

    • 逆序对连边生成的边集为 E0E_0,相邻连边生成的边集为 E1E_1
    • 简写 (l,j)(l, j)(r,j)(r, j) 连双向边为 lrl \Longleftrightarrow r

    下面证明二者等价。

    若有 al,j>ar,ja_{l, j} > a_{r, j},那么有 lrl \Longleftrightarrow r,而对于 i[l,r)i \in [l, r),一定有 pre[i]>suf[i+1]pre[i] > suf[i + 1],那也就是说,E1E_1 会包含 $l \Longleftrightarrow l + 1, \ \ l + 1 \Longleftrightarrow l + 2, \ \ \cdots, \ \ r - 1 \Longleftrightarrow r$,这相当于包含了一条 lrl \Longleftrightarrow r 的双向边,所以 E0E1E_0 \subseteq E_1

    若有 pre[i]>suf[i+1]pre[i] > suf[i + 1],那么有 ii+1i \Longleftrightarrow i + 1,同时,由前缀最大和后缀最小的性质可以得到,必然存在 l[1,i]l \in [1, i]r(i,n]r \in (i, n],使得 al,j>ar,ja_{l, j} > a_{r, j},那么首先就有 lrl \Longleftrightarrow r。如果 l<il < i,说明 al,j>ai,ja_{l, j} > a_{i, j},由题目条件有 lil \Longleftrightarrow i,同理如果 i+1<ri + 1 < r,那么 i+1ri + 1 \Longleftrightarrow r,将这三条边合起来,就包含了一条 ii+1i \Longleftrightarrow i + 1 的双向边,所以 E1E0E_1 \subseteq E_0

    综上,E0=E1E_0 = E_1,二者等价。

    设我们这样得到了 NN 个连通块,第 ii 个连通块的大小为 sizisiz_i,其块内最大值为 maxi\max_i,则最终答案为

    i=1Nsizi×maxi.\sum\limits_{i = 1}^{N}siz_i \times max_i.

    时间复杂度 O(nmα(nm))O(\rm{nm \cdot \alpha(nm)})

    • 其中 α(nm)\rm{\alpha(nm)} 是反阿克曼函数,一般可以看作 3,43, 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;
    }
    
    • 1

    信息

    ID
    12019
    时间
    5000ms
    内存
    512MiB
    难度
    4
    标签
    递交数
    0
    已通过
    0
    上传者