[Gym-101482] G - Gathering(三分套三分+前缀和)

题意

2维平面给n个整点, 找出一个整点(x,y)使得这个整点到其他n个点的曼哈顿距离和最小,同时需要满足每个点到这个(x,y)的曼哈顿距离不超过d.
n ≤ 1 e 5 , 0 ≤ x i , y i ≤ 1 e 9 , 0 ≤ d ≤ 2 e 9 n\le 1e5, 0 \le x_i, y_i \le 1e9, 0 \le d \le 2e9 n1e5,0xi,yi1e9,0d2e9

解题思路

如果没有"每个点到这个(x,y)的曼哈顿距离不超过d."的限制,则易得(x的中位数,y的中位数)是最优点.
考虑限制,我们发现每个点都有一个菱形的可选区域,这n个菱形的可选区域的交集就是我们可以选择的区域. 这个区域其实可以看成4条直线框起来的区域,那么我们维护这4条直线就可以得到合法区域.
注意到,当x确定的时候,答案关于y的函数是一个凹函数, 同样的, 当x变化的时候, y作为每个x的最优选取位置,则答案关于x的函数也是一个凹函数,所以三分套三分就做完了.
在求答案的时候,可以利用二分+前缀和快速得到n个点到指定点的曼哈顿距离和.

#include
#define ll long long
#define pb push_back
#define lowbit(x) ((x)&(-(x)))
#define mid ((l+r)>>1)
#define lson rt<<1, l, mid
#define rson rt<<1|1, mid+1, r
#define fors(i, a, b) for(int i = (a); i < (b); ++i)
#define P pair
using namespace std;
const int maxn = 1e5 + 50;
const ll inf = 0x3f3f3f3f3f3f3f3f;
int n;
ll xsum[maxn], ysum[maxn];
ll x[maxn], y[maxn];
ll d;
ll L = -inf, R = inf;//x-y
ll D = -inf, U = inf;//x+y
ll f(ll a, ll b[], ll c[]){
    if(a > b[n]){
        return (ll)n*a - c[n];
    }else if(a < b[0]){
        return c[n]-n*a;
    }else{
        int p = lower_bound(b+1, b+1+n, a)-b;
        return (c[n] - c[p-1] - (ll)(n-p+1)*a) + (ll)(p-1)*a - c[p-1];
    }
}
bool in(int x, int y){
    ll t1 = x-y;
    ll t2 = x+y;
    //cout<<"L:"<
    return L<=t1 && t1 <= R && D <= t2 && t2 <= U;
}
ll sol(ll a){
    ll l = max(a-R, D-a);
    ll r = min(a-L, U-a);
    if(r < l) return inf;
    ll ans = inf;
    while(r-l >= 3){
        ll lmid = l + (r-l)/3;
        ll rmid = r - (r-l)/3;
        ll la = f(lmid, y, ysum), ra = f(rmid, y, ysum);
        if(la >= ra) ans = min(ra, ans), l = lmid;
        else ans = min(ans, la), r = rmid;
    }
    while(l <= r) ans = min(ans, f(l, y, ysum)), l++;
    return ans;
}
int main()
{
    scanf("%d", &n);
    fors(i, 1, n+1) {
        scanf("%lld%lld", &x[i], &y[i]);
    }
    cin>>d;
    fors(i, 1, n+1){
        L = max(L, x[i] - y[i]-d);
        R = min(R, x[i] - y[i]+d);
        D = max(D, x[i] + y[i]-d);
        U = min(U, x[i] + y[i]+d);
    }
    sort(x+1, x+1+n); sort(y+1, y+1+n);
    fors(i, 1, n+1) xsum[i] = xsum[i-1] + x[i], ysum[i] = ysum[i-1] + y[i];
    if(in(x[n/2], y[n/2])){
        cout<<f(x[(n+1)/2], x, xsum)+f(y[(n+1)/2], y, ysum)<<endl;
    }else if(U < D || R < L){
        printf("impossible\n");
    }else {
        ll ans = inf;
        ll l = (D+L+1)/2;
        ll r = (R+U)/2;
        //cout<<"l:"<
        while(r-l >= 3){
            ll lmid = l + (r-l)/3;
            ll rmid = r - (r-l)/3;
            ll la = sol(lmid) + f(lmid, x, xsum);
            ll ra = sol(rmid) + f(rmid, x, xsum);
            if(la >= ra) ans = min(ans, ra), l = lmid;
            else ans = min(ans, la), r = rmid;
        }
        while(l <= r) ans = min(ans, sol(l)+f(l, x, xsum)), l++;
        cout<<ans<<endl;
    }
}
/*
5
3 1
4 1
5 9
2 6
5 3
4

*/

你可能感兴趣的:(二分)