「BZOJ4242」水壶

传送门
Luogu团队题链接

解题思路

考虑以图中的建筑为点建一张图,那么我们就只要求出这个图的 \(\text{Kruskal}\) 重构树然后按套路搞就行了。
重点是优化连边,因为直接连边边数就会太多贼JB多
考虑 \(\text{BFS}\),我们把所有建筑都作为源点跑 \(\text{BFS}\),求出每个点的距离他最近的建筑以及这段距离。
然后只要两个相邻点的最近建筑不同,就把这两个最近建筑连边,边数就优化到了 \(O(4\times H\times W)\) 级别(思想参照这题

细节注意事项

  • 这题我调了一上午,原因有:
    太菜:并查集忘记合并。
    太菜:忘记倍增。
    太菜:忘记判 \(-1\) 。

参考代码

#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdlib>
#include <cstdio>
#include <cctype>
#include <cmath>
#include <ctime>
#include <queue>
#define rg register
using namespace std;
template < typename T > inline void read(T& s) {
    s = 0; int f = 0; char c = getchar();
    while (!isdigit(c)) f |= c == '-', c = getchar();
    while (isdigit(c)) s = s * 10 + (c ^ 48), c = getchar();
    s = f ? -s : s;
}

typedef long long LL;
const int _ = 2002;
const int __ = 200002;
const int dx[] = { 0, 0, 1, -1 };
const int dy[] = { 1, -1, 0, 0 };

queue < pair < int, int > > Q;

int n, m, p, q, node;
int a[_][_], id[_][_]; LL dis[_][_];
int dep[__ * 2], fa[22][__ * 2]; LL val[__ * 2];

struct Edge { int x, y; LL w; }edge[_ * _ * 2]; int cnt;
inline bool cmp(const Edge& x, const Edge& y) { return x.w < y.w; }

int Fa[__ * 2];
inline int findd(int x) { return Fa[x] == x ? x : Fa[x] = findd(Fa[x]); }

inline int get(int x) { return !x || dep[x] ? dep[x] : dep[x] = get(fa[0][x]) + 1; }

inline void Kruskal() {
    for (rg int i = 1; i <= p; ++i) Fa[i] = i;
    sort(edge + 1, edge + cnt + 1, cmp);
    node = p;
    for (rg int i = 1; i <= cnt; ++i) {
        int fx = findd(edge[i].x);
        int fy = findd(edge[i].y);
        if (fx == fy) continue;
        ++node;
        Fa[node] = Fa[fx] = Fa[fy] = node;
        fa[0][fx] = fa[0][fy] = node, val[node] = edge[i].w;
    }
    for (rg int i = 1; i <= node; ++i) get(i);
    for (rg int i = 1; i <= 20; ++i)
        for (rg int j = 1; j <= node; ++j)
            fa[i][j] = fa[i - 1][fa[i - 1][j]];
}

inline LL LCA(int x, int y) {
    if (findd(x) != findd(y)) return -1;
    if (dep[x] < dep[y]) swap(x, y);
    for (rg int i = 20; ~i; --i) if (dep[fa[i][x]] >= dep[y]) x = fa[i][x];
    if (x == y) return x;
    for (rg int i = 20; ~i; --i) if (fa[i][x] != fa[i][y]) x = fa[i][x], y = fa[i][y];
    return val[fa[0][x]];
}

int main() {
#ifndef ONLINE_JUDGE
    freopen("in.in", "r", stdin);
#endif
    read(n), read(m), read(p), read(q); 
    char s[_]; 
    for (rg int i = 1; i <= n; ++i) {
        scanf("%s", s + 1); 
        for (rg int j = 1; j <= m; ++j) a[i][j] = s[j] == '.'; 
    }
    memset(dis, -1, sizeof dis); 
    for (rg int x, y, i = 1; i <= p; ++i)
        read(x), read(y), Q.push(make_pair(x, y)), id[x][y] = i, dis[x][y] = 0; 
    while (!Q.empty()) {
        pair < int, int > x = Q.front(); Q.pop(); 
        int i = x.first, j = x.second; 
        for (rg int ni, nj, k = 0; k < 4; ++k) {
            ni = i + dx[k], nj = j + dy[k]; 
            if (ni < 1 || ni > n || nj < 1 || nj > m) continue; 
            if (!a[ni][nj]) continue; 
            if (id[ni][nj]) {
                if (id[i][j] != id[ni][nj])
                    edge[++cnt] = (Edge) { id[i][j], id[ni][nj], dis[i][j] + dis[ni][nj] };
            } else
                dis[ni][nj] = dis[i][j] + 1, id[ni][nj] = id[i][j], Q.push(make_pair(ni, nj)); 
        }
    }
    Kruskal();
    for (rg int x, y; q--; ) read(x), read(y), printf("%lld\n", LCA(x, y)); 
    return 0;
}

完结撒花 \(qwq\)

上一篇:情感分析介绍


下一篇:C# Global.asax文件里实现通用防SQL注入漏洞程序(适应于post/get请求)