• bzoj4039 集会


    传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=4039

    【题解】

    曼哈顿距离没法把坐标分开来计算,使得$x$部分最小,不一定就能使得$y$部分最小。

    只能先转成切比雪夫距离(是可以实现上面的功能的),那么每个点的可行区域就是一个边平行于坐标轴的正方形。

    求一下正方形的交,那么答案肯定在交中,交为空则impossible。

    既然坐标独立,那么可以三分$x$坐标,再三分$y$坐标,这两个肯定是单峰函数。

    由于一些奇怪的原因这题卡常,暴力写是$O(nlog^2n)$(三分常数有点大),过不去。

    考虑优化,再把切比雪夫距离转成曼哈顿距离,这样就能快速计算曼哈顿距离和了。

    只要分成x和y坐标考虑,然后每次询问,二分第一个大于x的,稍微统计下就行了。

    最后我们三分的答案不一定是整数,在附近找一些点判下就行了。

    复杂度$O(nlogn+log^3n)$

    # include <stdio.h>
    # include <assert.h>
    # include <string.h>
    # include <iostream>
    # include <algorithm>
    // # include <bits/stdc++.h>
    
    using namespace std;
    
    typedef long long ll;
    typedef long double ld;
    typedef unsigned long long ull;
    const int M = 2e5 + 10;
    const int mod = 1e9+7;
    const ll inf = 1e16;
    
    int n, d;
    ll xl, xr, yl, yr, x[M], y[M];
    struct pa {
        ll x, y;
        pa() {}
        pa(ll x, ll y) : x(x), y(y) {}
    }p[M];
    
    # define abs(x) ((x) >= 0 ? (x) : -(x))
    
    struct Manhattan {
        ll p[M], s[M];
        inline void set() {
            memset(s, 0, sizeof s); 
            for (int i=1; i<=n; ++i) s[i] = s[i-1] + p[i];
        }
        inline ll gs(int x, int y) {
            if(x>y) return 0;
            return s[y] - s[x-1];
        }
        inline ll query(ll x) { 
            if(x >= p[n]) return (ll)n*x-s[n];    
            int l = 1, r = n, mid, pos;
            while(1) {
                if(r-l <= 3) {
                    for (int i=l; i<=r; ++i) {
                        if(p[i] > x) {
                            pos = i; 
                            break;
                        }
                    }
                    break;
                }
                mid = l+r>>1;
                if(p[mid] > x) r = mid;
                else l = mid;
            }
            ll ret = (ll)(pos-1) * x - gs(1, pos-1) + gs(pos, n) - (ll)(n-pos+1) * x;
            return ret;
        }
    }X, Y;
    
    inline ll calc(ll x, ll y) { 
    //    ll ret = 0;
    //    for (int i=1; i<=n; ++i)
    //        ret += max(abs(x - p[i].x), abs(y - p[i].y));
    //    return ret*2;  
        return X.query(x+y) + Y.query(x-y);
    }
    
    inline ll g(ll x) {
        ll l = yl, r = yr, mid1, mid2, t = inf, tmp, ans;
        while(1) {
            if(r-l <= 3) {
                for (ll i=l; i<=r; ++i) if((tmp = calc(x, i)) < t) t = tmp, ans = i;
                return ans;
            }
            mid1 = l+(r-l)/3.0, mid2 = r-(r-l)/3.0;
            if(calc(x, mid1) < calc(x, mid2)) r = mid2;
            else l = mid1;
        }
        assert(0); 
    }
    
    inline void sol() {
        xl = yl = -inf, xr = yr = inf;
        for (int i=1; i<=n; ++i) {
            scanf("%lld%lld", &x[i], &y[i]);
            p[i] = pa(x[i] + y[i], x[i] - y[i]);
        }
        sort(x+1, x+n+1); sort(y+1, y+n+1);
        for (int i=1; i<=n; ++i) X.p[i] = (ll)x[i] * 2, Y.p[i] = (ll)y[i] * 2;
        X.set(), Y.set();
        cin >> d;
        for (int i=1; i<=n; ++i) {
            xl = max(xl, p[i].x-d);
            yl = max(yl, p[i].y-d);
            xr = min(xr, p[i].x+d);
            yr = min(yr, p[i].y+d);
        }
        if(xl > xr || yl > yr) {
            puts("impossible");
            return ;
        }
        ll l = xl, r = xr, mid1, mid2, r1, r2, t = inf, tmp, ans;
        while(1) {
            if(r-l <= 3) {
                for (ll i=l; i<=r; ++i) if((tmp = calc(i, g(i))) < t) t = tmp, ans = i;
                break;
            }
            mid1 = l+(r-l)/3.0, mid2 = r-(r-l)/3.0;
            r1 = g(mid1), r2 = g(mid2);
            if(calc(mid1, r1) < calc(mid2, r2)) r = mid2;
            else l = mid1;
        }
        l = ans; r = g(l);
        ans = inf;
        for (ll i=l-2, t; i<=l+2; ++i)
            for (ll j=r-2; j<=r+2; ++j) {
                if(i < xl || i > xr || j < yl || j > yr) continue;
                if((i+j)&1) continue;
                if((t = calc(i, j)) < ans) 
                    ans = t; 
            }
        cout << ans/2 << endl;
    }
    
    int main() {
        while(cin >> n && n) sol();
        return 0;
    }
    View Code
  • 相关阅读:
    轻松自动化---selenium-webdriver(python) (八)
    Ubuntu 18.04 LTS 启用 WakeOnLAN
    lower_bound 和 upper_bound
    [LeetCode 201.] Bitwise AND of Numbers Range
    [LeetCode 162.] Find Peak Element
    [LeetCode 33. 81. 153. 154.] 旋转数组中的二分查找
    C++ unordered_map 的一个疑问
    [LintCode 386.] 最多有k个不同字符的最长子字符串
    [LintCode 550.] 最常使用的K个单词II
    [LintCode 1029.] 寻找最便宜的航行旅途(最多经过k个中转站)
  • 原文地址:https://www.cnblogs.com/galaxies/p/bzoj4039.html
Copyright © 2020-2023  润新知