Mummy Madness 题解

扫描线。

首先,如果时间确定,那么你能够到达的所有位置组合起来一定是一个正方形,木乃伊也一样。

接着尝试利用这一点来判断某个时刻你是否会被木乃伊抓住。

如果你能够到达的位置里,存在一个位置使得没有一个木乃伊能够到达,那么你肯定是可以活下来的,只需要往这个位置走就好了。

会不会存在“在到达这个位置前就被木乃伊抓住”的情况呢?肯定不会,因为你被木乃伊抓住时肯定是和它在同一时刻的同一位置,如果把这个位置看作一个新的起点,那么之后你能走到的位置木乃伊也同样能够走到,这样就与“存在一个位置使得没有一个木乃伊能够到达”相悖了。

如果你所有能到达的位置木乃伊一样能够到达,那么你肯定逃脱不了,这一点是显而易见的。

再来考虑一点:如果某个时刻你是一定会被抓住的,那么后面的时刻你也是一定会被抓住的。如果某一时刻你是一定不会被抓住的,那么前面的时刻你也一定不会被抓住。

这看起来是一句废话,但是它提示我们“每个时刻会不会被抓”满足二段性,可以二分答案。

思考二分答案时如何判断,也就是如何判断当前时刻你能够到达的点是否全部包含于木乃伊能够到达的点。

其实做法非常简单,就是求“你能到达的范围与木乃伊能到达的范围的交集大小”,如果这个交集大小和你能到达的范围大小相同,那么你能到达的位置肯定是全部包含于木乃伊能够到达的位置的。

这是一个求若干矩形的面积并的问题,扫描线解决即可。

代码,我的线段树维护的区间是左闭右开的,注意一下:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int Case, n, l, r, mid, ans, x_1, y_1, x_2, y_2, x[100005], y[100005];
vector<int> lsh;
struct scanline {
    int xl, xr, y, v;
    scanline(int _xl = 0, int _xr = 0, int _y = 0, int _v = 0): xl(_xl), xr(_xr), y(_y), v(_v) {}
    bool operator < (const scanline& _) const {
        return y < _.y;
    }
};
vector<scanline> line;
struct Segment_Tree {
    int L, R, v, l[800005], r[800005], len[800005], sum[800005];
    #define lc (k << 1)
    #define rc (lc | 1)
    #define mid ((l[k] + r[k]) >> 1)
    void build(int k) {
        len[k] = sum[k] = 0;
        if(l[k] + 1 >= r[k]) return;
        l[lc] = l[k], r[lc] = l[rc] = mid, r[rc] = r[k];
        build(lc), build(rc);
    }
    void push_up(int k) {
        len[k] = 0;
        if(sum[k]) len[k] = lsh[r[k]] - lsh[l[k]];
        else if(l[k] + 1 < r[k]) len[k] = len[lc] + len[rc];
    }
    void change(int k) {
        if(L <= l[k] && r[k] <= R) {
            sum[k] += v;
            return push_up(k);
        }
        if(L < mid) change(lc);
        if(R > mid) change(rc);
        push_up(k);
    }
    void change(int lt, int rt, int val) {
        L = lower_bound(lsh.begin(), lsh.end(), lt) - lsh.begin(), R = lower_bound(lsh.begin(), lsh.end(), rt) - lsh.begin(), v = val;
        return change(1);
    }
    #undef mid
} tree;
bool check(int c) {
    line.clear(), lsh.clear();
    for(int i = 1; i <= n; ++i) {
        x_1 = max(x[i] - c, -c), x_2 = min(x[i] + c + 1, c + 1), y_1 = max(y[i] - c, -c), y_2 = min(y[i] + c + 1, c + 1);
        if(x_1 < x_2 && y_1 < y_2) {
            line.push_back(scanline(x_1, x_2, y_1, 1));
            line.push_back(scanline(x_1, x_2, y_2, -1));
            lsh.push_back(x_1);
            lsh.push_back(x_2);
        }
    }
    stable_sort(line.begin(), line.end());
    stable_sort(lsh.begin(), lsh.end());
    lsh.erase(unique(lsh.begin(), lsh.end()), lsh.end());
    tree.l[1] = 0, tree.r[1] = lsh.size() - 1;
    tree.build(1);
    const unsigned sz = line.size();
    ll sum = 0;
    for(unsigned i = 0; i < sz; ++i) {
        tree.change(line[i].xl, line[i].xr, line[i].v);
        while(i < sz - 1 && line[i].y == line[i + 1].y) {
            ++i;
            tree.change(line[i].xl, line[i].xr, line[i].v);
        }
        if(i < sz - 1) {
            sum += (ll)(line[i + 1].y - line[i].y) * tree.len[1];
        }
    }
    return sum == (2ll * c + 1ll) * (2ll * c + 1ll);
}
int main() {
    ios::sync_with_stdio(0);
    cin.tie(0), cout.tie(0);
    while(cin >> n) {
        if(!(~n)) break;
        for(int i = 1; i <= n; ++i) {
            cin >> x[i] >> y[i];
        }
        l = 0, r = 1000000, ans = -1;
        while(l <= r) mid = (l + r) >> 1, check(mid) ? (ans = mid, r = mid - 1) : (l = mid + 1);
        cout << "Case " << ++Case << ": ";
        if(~ans) cout << ans << '\n';
        else cout << "never\n";
    }
    return 0;
}
posted @ 2023-12-05 20:17  A_box_of_yogurt  阅读(2)  评论(0编辑  收藏  举报  来源
Document