P5926 [JSOI2009] 面试的考验

lxl 上课讲的题,来写个题解。

双倍经验:CF765F

三倍经验:CF1793F

题目传送门

  • 给出序列 \(a_1\sim a_n\),有 \(q\) 次询问,每次询问给出 \(l,r\),求两个数 \(i,j\,(l\le i,j\le r)\) 满足 \(a_i\ne a_j\)\(|a_i-a_j|\) 最小。输出这个最小值。

  • \(n,q\le 10^5\)。设值域为 \(V\)\(|V|\le 10^9\)

不妨令 \(i<j\)。称 \(|a_i-a_j|\) 为点对 \((i,j)\) 的权值。

一个朴素的想法是,把所有的点对 \((i,j)\) 的值计算出来,然后就是做满足 \(l\le i\)\(j\le r\) 的二维偏序。

但是真的所有 \((i,j)\) 都有贡献吗?考虑这样一种情况:\(i<j_1<j_2\)\(a_i< a_{j_1}\le a_{j_2}\),若 \([l,r]\) 包含了 \((i,j_2)\) 这个点对,就一定包含了 \((i,j_1)\) 这个点对,而且 \(|a_i-a_{j_1}|\le |a_i-a_{j_2}|\),所以 \((i,j_2)\) 这个点对没有贡献。这时我们称 \(\boldsymbol{(i,j_1)}\) 支配了 \(\boldsymbol{(i,j_2)}\)

简单理解一下,一个点对能够支配另一个点对,当且仅当这个点对的限制更宽松,且权值不超过另一个点对

考虑把所有有贡献的点对 \((i,j)\) 找出来。先固定左点 \(i\) 按顺序一个个从左往右找。分为两种情况:

  • \(a_i<a_j\),此时权值 \(|a_i-a_j|=a_j-a_i\)

    设当前找到 \((i,p)\) 是有贡献的点对,考虑怎样的 \((i,q)\)(满足 \(p<q\))仍然有贡献。首先 \(a_i<a_q<a_p\),不然 \(\boldsymbol{(i,p)}\) 会支配 \(\boldsymbol{(i,q)}\)

    其次,\(a_q-a_i<a_p-a_q\),不然 \(\boldsymbol{(p,q)}\) 会支配 \(\boldsymbol{(i,q)}\)。此时我们将不等式变形,会发现 \(\boldsymbol{a_q-a_i< \dfrac{a_p-a_i}{2}}\),即权值至少减半。因此以 \(i\) 为左点的有贡献点对不会超过 \(\boldsymbol{\mathcal{O}(\log|V|)}\)

    因为找到 \(\mathcal{O}(\log |V|)\) 个点对后,权值已经变成 \(0\),由于点对的权值是绝对值,显然不可能再找到一个点对,使得它的值小于 \(0\)

    此时,\(a_i<a_q<\dfrac{a_i+a_p}{2}\)

  • \(a_i>a_j\) 的情况类似分析即可。

这么一来,需要保留的点对就只有 \(\mathcal{O}(n\log|V|)\) 个。更严谨地来讲,与其说上面的过程是在找出所有有贡献的点对,不如说是去除一些点对,使得剩下的点对包含最优解,且数量可以接受。

为了配合上述过程找出有贡献点对,我们需要支持这个操作:

查询一个最小的位置 \(x\),使得 \(x>y\)\(a_x\in[u,v]\)

没懂这一步大佬们是如何不可持久化做的,只能来介绍一下我的憨憨做法。建立一棵可持久化权值线段树,版本 \(i\) 的线段树维护的是每种权值在 \([i,n]\) 中出现的最左位置,节点内维护区间最小值。若某种权值未出现,则设为无穷大。查询的时候,在 \(y+1\) 版本查询 \([u,v]\) 的最小值。

设为未出现的权值设为无穷大的意义就体现出来了,我要查询的是出现过的数的最小位置,没出现过的数因为权值无穷大,会在取最小值时被舍去。若查询结果为最小值,则不存在这样的数,说明不能找到更多的点对了。

把所有有贡献的点对保留后,离线询问,将第一维降序排序(修改在前),用权值树状数组维护第二维的限制。

时间复杂度为 \(\mathcal{O}(n\log^2|V|)\),空间复杂度为 \(\mathcal{O}(n\log |V|)\)

提交记录

#include <bits/stdc++.h>
#define lb(x) ((x) & (-(x)))
using namespace std; const int N = 3e5 + 2, inf = 0x3f3f3f3f, L = 1, R = 1e9; 
int n, a[N], m, cnt, ans[N << 2];
template<class T> void read(T &x) {
    x = 0; T f = 1; char c = getchar();
    for (; !isdigit(c); c = getchar()) if (c == '-') f = -1;
    for (; isdigit(c); c = getchar()) x = (x << 3) + (x << 1) + c - 48; x *= f;
}
template<class T> void write(T x) {
    if (x > 9) write(x / 10); putchar(x % 10 + 48);
}
template<class T> void print(T x, char ed = '\n') {
    if (x < 0) putchar('-'), x = -x; write(x), putchar(ed);
}
struct ChairmanTree {
    int ls[N * 20], rs[N * 20], mn[N * 20], rt[N], cnt;
    void init() {
        memset(ls, 0, sizeof ls); memset(mn, 0x3f, sizeof mn);
        memset(rs, 0, sizeof rs); memset(rt, cnt = 0, sizeof rt);
    }
    void modify(int &x, int y, int l, int r, int k, int v) {
        x = ++cnt; mn[x] = min(mn[y], v); 
        if (l == r) return; int mid = (l + r) >> 1;
        if (k <= mid) rs[x] = rs[y], modify(ls[x], ls[y], l, mid, k, v);
        else ls[x] = ls[y], modify(rs[x], rs[y], mid + 1, r, k, v);
    }
    int query(int x, int l, int r, int ql, int qr) {
        if (!x || ql > qr) return inf; 
        if (ql <= l && r <= qr) return mn[x]; int mid = (l + r) >> 1, ret = inf;
        if (ql <= mid) ret = query(ls[x], l, mid, ql, qr);
        if (qr > mid) ret = min(ret, query(rs[x], mid + 1, r, ql, qr)); return ret;
    }
} T;
struct BinaryIndexedTree {
    int mn[N]; void init() { memset(mn, 0x3f, sizeof mn); }
    void modify(int x, int v) { 
        while (x <= n) mn[x] = min(mn[x], v), x += lb(x); 
    }
    int query(int x) {
        int ret = inf; while (x) ret = min(ret, mn[x]), x -= lb(x); return ret;
    }
} B;
struct node { int x, y, id; } p[N * 25]; 
signed main() {
    read(n); read(m); for (int i = 1; i <= n; ++i) read(a[i]); T.init();
    T.modify(T.rt[n], T.rt[n + 1], L, R, a[n], n); B.init();
    for (int i = n - 1, v; i >= 1; --i) {
        int pos = T.query(T.rt[i + 1], L, R, a[i] + 1, R);
        while (pos != inf) {
            v = ((a[i] + a[pos]) >> 1) - (((a[i] + a[pos]) & 1) ^ 1);
            p[++cnt] = {i, pos, 0}; 
            pos = T.query(T.rt[pos + 1], L, R, a[i] + 1, v);
        }
        T.modify(T.rt[i], T.rt[i + 1], L, R, a[i], i);
    }
    T.init(); T.modify(T.rt[n], T.rt[n + 1], L, R, a[n], n); 
    for (int i = n - 1, v; i >= 1; --i) {
        int pos = T.query(T.rt[i + 1], L, R, L, a[i] - 1);
        while (pos != inf) {
            p[++cnt] = {i, pos, 0}; v = ((a[i] + a[pos]) >> 1) + 1;
            pos = T.query(T.rt[pos + 1], L, R, v, a[i] - 1);
        }
        T.modify(T.rt[i], T.rt[i + 1], L, R, a[i], i);
    }
    for (int i = 1, l, r; i <= m; ++i) { read(l), read(r); p[++cnt] = {l, r, i}; }
    stable_sort(p + 1, p + cnt + 1, [&](node u, node v) { 
        return u.x != v.x ? u.x > v.x : !u.id; 
    });
    for (int i = 1; i <= cnt; ++i)
        if (p[i].id) ans[p[i].id] = B.query(p[i].y);
        else B.modify(p[i].y, abs(a[p[i].x] - a[p[i].y]));
    for (int i = 1; i <= m; ++i) print(ans[i]); return 0;
}
posted @ 2023-10-05 18:14  lzyqwq  阅读(55)  评论(1)    收藏  举报