C. Cellular Network

https://codeforces.com/contest/702/problem/C

题意:给定n个城市坐标和m个tower,问tower的最小半径是多少,可以罩住所有的城市。

思路:二分半径,优先更新上界。难点在于如何判定当前半径r是否可以包含所有城市,可以采取这个方法:求出m个区间,然后对区间进行合并,再依次考虑每个城市坐标,再次使用二分,看当前城市坐标是否属于某个区间即可。

总结:1、为了避免二分出现负数,对所有的坐标进行了偏移,很不错。2、二分的上界1e9不够用,为什么?因为偏移后最大的坐标应该是2e9,最小的是0,那么r应该至少为2e9,但是为了更无脑一点,直接开到最大即可。
3、在区间上二分的时候,要么找upper_bound的前一个数,要么直接lower_bound的时候,给城市的坐标 + 1,这样是为了找到区间左端点在城市坐标左边的区间,如果找不到,返回的应该是end。

inline void solve() {
    int n, m;
    cin >> n >> m;

    constexpr int shift = 1e9;
    vector<int> a(n);
    for (auto& x : a) {
        cin >> x;
        x += shift;
    }

    vector<long long> tow(m);
    for (auto& x : tow) {
        cin >> x;
        x += shift;
    }

    sort(tow.begin(), tow.end());

    auto valid = [&](long long x) {
        vector<pair<long long, long long>> b;
        b.reserve(m);
        for (int i = 0; i < m; ++i) {
            pair<long long, long long> t = {tow[i] - x, tow[i] + x};
            if (!b.empty() && t.first <= b.back().second) {
                b.back().second = t.second;
            }
            else {
                b.push_back(std::move(t));
            }
        }
        bool ok = true;
        for (int i = 0; i < n; ++i) {
            auto it = lower_bound(b.begin(), b.end(), pair<long long, long long>{a[i] + 1, 0});
            if (it != b.begin()) {
                it = prev(it);
            }
            if (it->first <= a[i] && it->second >= a[i]) {
                continue;
            }
            else {
                ok = false;
                break;
            }
        }
        return ok;
    };

    long long l = 0, r = 1e18;
    while (l < r) {
        long long mid = (l + r) >> 1;
        if (valid(mid)) {
            r = mid;
        }
        else {
            l = mid + 1;
        }
    }

    cout << l << '\n';
}
posted @ 2025-03-20 10:20  _Yxc  阅读(10)  评论(0)    收藏  举报