其他分享
首页 > 其他分享> > CF765F Souvenirs

CF765F Souvenirs

作者:互联网

给出 \(n\) 以及一个长为 \(n\) 的序列 \(a\)。

给出 \(m\),接下来 \(m\) 组询问。

每组询问给出一个 \(l,r\),你需要求出,对于 \(i,j \in [l,r]\),且满足 \(i \neq j\),\(|a_i-a_j|\) 的最小值。

\(1 \leq n \leq 10^5\),\(1 \leq m \leq 3\times 10^5\),\(0 \leq a_i \leq 10^9\)。

sol

感觉做分块顺手了许多,不到一小时。

首先看到这题类似于第四分块,于是考虑分块。

考虑对序列分块,取块长为 \(\sqrt n\),便利计算时间复杂度,后续可手动调整。

按照套路,先想区间在同一个块内的答案,块内排序,然后暴力计算即可,时间复杂度 \(\mathcal O(\sqrt n)\)。

如果区间不在同一个块,则分为三个部分:左散块,中多整块,右散块(都是套路了吧)。

记 \(s[i][j]\) 表示第 \(i\) 块到第 \(j\) 块的答案,\(f[i][j]\) 表示 \(i\) 在块内的 \(i\) 前缀与第 \(j\) 块到 \(i\) 的上一块的答案,\(g[i][j]\) 表示 \(i\) 所在块内的 \(i\) 后缀与 \(i\) 的下一块到第 \(j\) 块的答案。

对于中多整块的答案,直接查询 \(s\) 数组,时间复杂度 \(\mathcal O(1)\)。

对于左散块及右散块对中间块的答案,直接查询 \(f,g\) 数组,时间复杂度 \(\mathcal O(1)\)。

对于左、右散块的内部答案,由于排序后是有序的,归并双指针计算即可,时间复杂度 \(\mathcal O(\sqrt n)\)。

最后考虑预处理 \(f,g,s\) 。

对于 \(f,g\),先计算每个数与每个块的答案,然后前、后缀一下即可,时间复杂度为 \(\mathcal O(n \sqrt n)\)(注意此时有序,使用双指针优化,时间复杂度不会达到 \(\mathcal O(n^2)\))。

那么 \(s\) 可由 \(f\) 递推而来,时间复杂度 \(\mathcal O(n)\)。

那么总时空复杂度为 \(\mathcal O(n\sqrt n)\)。

注意,代码中 \(f\) 数组是把上面的 \(f,g\) 结合成的,优化空间。

\(\text{45.72s / 124.22MB / 3.90KB C++20 O2}\),稍逊于线段树,不过优势是可强制在线。

#include <bits/stdc++.h>

using namespace std;

#define re register

inline int read()
{
    re int x = 0, f = 1;
    re char c = getchar();
    while (c < '0' || c > '9')
    {
        if (c == '-')
            f = -1;
        c = getchar();
    }
    while (c >= '0' && c <= '9')
        x = x * 10 + c - '0', c = getchar();
    return x * f;
}

inline void write(re int x)
{
    if (x < 0)
    {
        putchar('-');
        x = -x;
    }
    if (x > 9)
        write(x / 10);
    putchar(x % 10 + '0');
}

inline void cmin(int &a, int b)
{
    a = b > a ? a : b;
}

const int _ = 1e5 + 2, BEL = 320;

int n, m, a[_], blo, ans[_];

int f[_][BEL], s[BEL][BEL], L[BEL], R[BEL];

pair<int, int> b[_];

vector<int> vl, vr;

inline int merge()
{
    re int l1 = 0, l2 = 0, ans = 0x3f3f3f3f;
    for (re int i = 0; i < vl.size() - 1; ++i)
        cmin(ans, vl[i + 1] - vl[i]);
    for (re int i = 0; i < vr.size() - 1; ++i)
        cmin(ans, vr[i + 1] - vr[i]);
    while (l1 < vl.size() && l2 < vr.size())
    {
        cmin(ans, abs(vl[l1] - vr[l2]));
        if (vl[l1] < vr[l2])
            ++l1;
        else
            ++l2;
    }
    return ans;
}

signed main()
{
    n = read();
    for (re int i = 1; i <= n; ++i)
        a[i] = read(), b[i] = {a[i], i};
    blo = (n - 1) / BEL + 1;
    for (re int i = 1; i <= blo; ++i)
        L[i] = R[i - 1] + 1, R[i] = i * BEL;
    R[blo] = n;
    for (re int i = 1; i <= blo; ++i)
        sort(b + L[i], b + R[i] + 1);
    for (re int i = 1; i <= blo; ++i)
    {
        for (re int j = 1; j <= blo; ++j)
            if (i != j)
                for (re int k = L[i], l = L[j]; k <= R[i]; ++k)
                {
                    while (l < R[j] && b[l + 1].first < b[k].first)
                        ++l;
                    f[b[k].second][j] = abs(b[k].first - b[l].first);
                    if (l < R[j])
                        cmin(f[b[k].second][j], abs(b[l + 1].first - b[k].first));
                }
        for (re int k = L[i]; k <= R[i]; ++k)
        {
            for (re int j = i - 2; j >= 0; --j)
                cmin(f[k][j], f[k][j + 1]);
            for (re int j = i + 2; j <= blo; ++j)
                cmin(f[k][j], f[k][j - 1]);
        }
        for (re int j = 1; j < i; ++j)
            for (re int k = L[i] + 1; k <= R[i]; ++k)
                cmin(f[k][j], f[k - 1][j]);
        for (re int j = i + 1; j <= blo; ++j)
            for (re int k = R[i] - 1; k >= L[i]; --k)
                cmin(f[k][j], f[k + 1][j]);
        s[i][i] = 0x3f3f3f3f;
        for (re int j = L[i]; j < R[i]; ++j)
            cmin(s[i][i], b[j + 1].first - b[j].first);
    }
    for (re int i = 1; i <= blo; ++i)
        for (re int j = i + 1; j <= blo; ++j)
            s[i][j] = min(min(s[i][j - 1], f[R[j]][i]), s[j][j]);
    m = read();
    re int l, r, bl, br, ans;
    while (m--)
    {
        l = read(), r = read();
        bl = (l - 1) / BEL + 1, br = (r - 1) / BEL + 1;
        ans = 0x3f3f3f3f;
        if (bl < br)
        {
            if (bl + 1 <= br - 1)
            {
                ans = s[bl + 1][br - 1];
                cmin(ans, min(f[r][bl + 1], f[l][br - 1]));
            }
            vl.clear(), vr.clear();
            for (re int i = L[bl]; i <= R[bl]; ++i)
                if (b[i].second >= l)
                    vl.push_back(b[i].first);
            for (re int i = L[br]; i <= R[br]; ++i)
                if (b[i].second <= r)
                    vr.push_back(b[i].first);
            cmin(ans, merge());
        }
        else
        {
            re int lst = -0x3f3f3f3f;
            for (re int i = L[bl]; i <= R[bl]; ++i)
                if (l <= b[i].second && b[i].second <= r)
                {
                    cmin(ans, b[i].first - lst);
                    lst = b[i].first;
                }
        }
        printf("%d\n", ans);
    }
}

标签:cmin,int,复杂度,vl,Souvenirs,re,CF765F,mathcal
来源: https://www.cnblogs.com/orzz/p/16099328.html