[2018 GCPC M] Mountaineers

https://vjudge.net/problem/Gym-102021M

题意:
给你一个最大为\(500*500\)的矩阵,你可以在矩阵中的点中上下左右移动。接下来最多\(10^5\)次询问,每次询问两个点,你要从第一个点走到第二个点,使得路程中经过的点权值的最大值最小,问你这个点权值是多少。

思路:
朴素的想法是二分权值,进行\(dfs\),但是时间复杂度是\(O(n^2 * q * log10^6)\)的。后来尝试加记忆化搜索,但是失败了。

既然无法在每次询问时将时间复杂度降下来,那么需要考虑能否将询问一起处理。这道题可以用\(LCA\)的方法解,大概的方法是优先处理点权较小的点,从四周点权不大于它的点向它建一条有向边,形成一棵树,这样就能保证一路上经过的点的权值最大值最小,并且拥有最大权值的点是它们的\(LCA\)。然后以权值最大的点为根,每次询问输出两个点的\(LCA\)的权值就行了。

这里介绍一种\(dsu\)的方法。我们将所有询问的编号记录在对应的点上。然后优先处理点权较小的点,每次将四周点权不大于它的点与它合并在并查集里,合并的时候看下它们是否有相同的询问编号,如果有,那么这次询问的答案就是当前点的权值。注意在合并的时候,我们要判断两个点拥有的询问数量,将小的合并在大的里面。因为我们在看两个点是否有相同的询问编号时,是需要进行遍历操作的,最坏的情况下时间复杂度还是会达到\(O(n^2 * q)\)。但如果我们每次都遍历拥有询问数量较小的点,遍历的总次数就会是\(O(q)\)级别的,所以不加上查询是否有相同的编号时,总时间复杂度降到了\(O(n^2 + q)\)。为了让各种操作都能方便进行,我们选择\(set\)来储存询问的信息,时间复杂度大概为\(O(n^2 + qlogq)\),但是常数比较大。

#include <bits/stdc++.h>

using namespace std;

#define endl "\n"

typedef long long ll;

const int inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3f;
const int Q = 1e5 + 7;
const int N = 500 * 500 + 7;

set<int> sz[N];
int n, m, q, w[N], fa[N], res[Q];
int mov[][2] = {0, 1, 0, -1, 1, 0, -1, 0};

bool check(int x, int y) {
    if (x < 1 ||x > n || y < 1 || y > m) return false;
    return true;
}

int ID(int x, int y) {
    return (x - 1) * m + y;
}

int find(int x) {
    return x == fa[x]? x : fa[x] = find(fa[x]);
}

void merge(int x, int y, int now) {
    x = find(x), y = find(y);
    if (x == y) return;
    if (sz[x].size() > sz[y].size()) swap(x, y);

    for (auto i : sz[x]) {
        if (sz[y].count(i)) {
            res[i] = now;
            sz[y].erase(i); // 删除操作,降低空间复杂度的同时也降低了时间复杂度
        } else {
            sz[y].insert(i);
        }
    }
    sz[x].clear();
    fa[x] = y;
}

void solve() {
    cin >> n >> m >> q;
    for (int i = 1; i <= n; ++i) {
        for (int j = 1; j <= m; ++j) {
            cin >> w[ID(i, j)];
            fa[ID(i, j)] = ID(i, j);
        }
    }

    for (int i = 1, x1, x2, y1, y2; i <= q; ++i) {
        cin >> x1 >> y1 >> x2 >> y2;
        if (x1 == x2 && y1 == y2) {
            res[i] = w[ID(x1, y1)];
            continue;
        }
        sz[ID(x1, y1)].insert(i);
        sz[ID(x2, y2)].insert(i);
    }

    vector< tuple<int, int, int> > v;
    for (int i = 1; i <= n; ++i) {
        for (int j = 1; j <= m; ++j) {
            v.emplace_back(make_tuple(w[ID(i, j)], i, j));
        }
    }
    sort(v.begin(), v.end());

    for (auto t : v) {
        int ww, xx, yy;
        tie(ww, xx, yy) = t;
        
        for (int i = 0; i < 4; ++i) {
            int fx = xx + mov[i][0], fy = yy + mov[i][1];
            if (check(fx, fy)) {
                if (w[ID(fx, fy)] > w[ID(xx, yy)]) continue;
                merge(ID(xx, yy), ID(fx, fy), ww);
            }
        }
    }

    for (int i = 1; i <= q; ++i) {
        cout << res[i] << endl;
    }
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);cout.tie(nullptr);
    cout << fixed << setprecision(20);
    int _T = 1;
    while (_T--) solve();
    return 0;
}

/*
3 5 3
1 3 2 1 3
2 4 5 4 4
2 1 3 2 2
1 1 3 2
2 4 2 2
1 4 3 4
*/
posted @ 2021-03-15 23:20  stff577  阅读(67)  评论(0)    收藏  举报