[洛谷 P3992] [BJOI2017]开车

[BJOI2017]开车

P3992

1

题目大意

给出 \(n\) 个车的位置 \(a_i\)\(n\) 个加油站的位置 \(b_i\) ,一辆车从 \(x\) 位置到 \(y\) 位置的代价为 \(|x - y|\) ,问如何两两配对使答案最小,\(q\) 次询问每次修改 \(a_x\)\(y\) ,每次修改后给出当前答案。

数据范围

\(n \le 50000, q \le 50000\)

时空限制

3000ms, 256MB, O2

反思

似乎根本没有任何思考就点开了题解

分析

首先发现答案实际上就是分别排序后对应配对,考虑维护数轴上每条边的贡献,发现我们记一个 \(a_i\)\(1\) ,$b_i $ 为 \(-1\) 的前缀和,那么它的贡献就是 \(|sum|\)

那么我们修改就相当于区间 \(\pm 1\) ,但是我们无法简单地维护一个绝对值,所以用分块的方法

将块内的前缀和排序,二分 \(0\) 点,带上标记即可修改

Code

#include <algorithm> 
#include <cmath>
#include <cstdio> 
#include <iostream>
using namespace std;
inline char nc() {
    static char buf[100000], *l = buf, *r = buf;
    return l==r&&(r=(l=buf)+fread(buf,1,100000,stdin),l==r)?EOF:*l++;
} 
template<class T> void read(T &x) {
    x = 0; int f = 1, ch = nc();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=nc();}
    while(ch>='0'&&ch<='9'){x=x*10-'0'+ch;ch=nc();}
    x *= f;
}
#define myabs(a) ((a) < 0 ? - (a) : (a))
typedef long long ll;
const int maxn = 50000 + 5;
const int maxq = 50000 + 5;
const int maxp = maxn * 2 + maxq;
const int maxb = 400;
int n, q, a[maxn], b[maxn]; ll an;
int H[maxp], p;
struct data {
    int x, y;
} ask[maxq];
void Hash() {
    sort(H + 1, H + p + 1);
    p = unique(H + 1, H + p + 1) - H - 1;
    for(int i = 1; i <= n; ++i) {
        a[i] = lower_bound(H + 1, H + p + 1, a[i]) - H;
    }
    for(int i = 1; i <= n; ++i) {
        b[i] = lower_bound(H + 1, H + p + 1, b[i]) - H;
    }
    for(int i = 1; i <= q; ++i) {
        ask[i].y = lower_bound(H + 1, H + p + 1, ask[i].y) - H;
    }
}
namespace blocks {
    int len;
    int L[maxb], R[maxb], bel[maxp];
    int sum[maxp], val[maxp];
    int sval[maxp], ord[maxp], tag[maxb];
    inline int cmp(const int &a, const int &b) {
        return sum[a] < sum[b];
    } 
    void rebuild(int x) {
        sort(ord + L[x], ord + R[x] + 1, cmp);
        sval[L[x]] = val[ord[L[x]]];
        for(int i = L[x] + 1; i <= R[x]; ++i) {
            sval[i] = sval[i - 1] + val[ord[i]];
        }
    }
    void init() {
        for(int i = 1; i <= n; ++i) {
            sum[a[i]]++, sum[b[i]]--;
        }
        for(int i = 1; i <= p; ++i) {
            if(i < p) val[i] = H[i + 1] - H[i];
            sum[i] += sum[i - 1];
            an += (ll)myabs(sum[i]) * val[i];
        }
        len = ceil(sqrt(p));
        for(int i = 1; i <= p; ++i) {
            int x = bel[i] = (i - 1) / len + 1;
            if(!L[x]) L[x] = i;
            R[x] = i;
        }
        for(int i = 1; i <= p; ++i) ord[i] = i;
        for(int i = 1; i <= bel[p]; ++i) {
            rebuild(i);
        }
    }
    void add(int x) {
        for(int i = x; i <= R[bel[x]]; ++i) {
            an += val[i] * (sum[i] + tag[bel[x]] >= 0 ? 1 : -1);
            sum[i]++;
        }
        rebuild(bel[x]);
        for(int i = bel[x] + 1; i <= bel[p]; ++i) {
            int l = L[i], r = R[i], re = -1;
            while(l <= r) {
                int mid = (l + r) >> 1;
                if(sum[ord[mid]] + tag[i] >= 0) r = mid - 1, re = mid;
                else l = mid + 1;
            }
            if(re == -1) {
                an -= sval[R[i]];
            }
            else if(re == L[i]) {
                an += sval[R[i]];
            }
            else {
                an -= sval[re - 1];
                an += sval[R[i]] - sval[re - 1];
            }
            tag[i]++;
        }
    }
    void sub(int x) {
        for(int i = x; i <= R[bel[x]]; ++i) {
            an += val[i] * (sum[i] + tag[bel[x]] <= 0 ? 1 : -1);
            sum[i]--;
        }
        rebuild(bel[x]);
        for(int i = bel[x] + 1; i <= bel[p]; ++i) {
            int l = L[i], r = R[i], re = -1;
            while(l <= r) {
                int mid = (l + r) >> 1;
                if(sum[ord[mid]] + tag[i] <= 0) l = mid + 1, re = mid;
                else r = mid - 1;
            }
            if(re == -1) {
                an -= sval[R[i]];
            }
            else if(re == R[i]) {
                an += sval[R[i]];
            }
            else {
                an += sval[re];
                an -= sval[R[i]] - sval[re];
            }
            tag[i]--;
        }
    }
}
int main() {
//  freopen("testdata.in", "r", stdin);
//  freopen("testdata.out", "w", stdout); 
    read(n);
    for(int i = 1; i <= n; ++i) {
        read(a[i]), H[++p] = a[i];
    }
    for(int i = 1; i <= n; ++i) {
        read(b[i]), H[++p] = b[i];
    }
    read(q); 
    for(int i = 1; i <= q; ++i) {
        read(ask[i].x), read(ask[i].y);
        H[++p] = ask[i].y;
    }
    Hash();
    blocks :: init();
    printf("%lld\n", an);
    for(int i = 1; i <= q; ++i) {
        int x = ask[i].x, y = ask[i].y;
        blocks :: sub(a[x]);
        blocks :: add(a[x] = y);
        printf("%lld\n", an);
    }
    return 0;
}

总结

利用分块维护绝对值

猜你喜欢

转载自www.cnblogs.com/ljzalc1022/p/10347164.html