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