CF765F Souvenirs
给出 以及一个长为 的序列 。
给出 ,接下来 组询问。
每组询问给出一个 ,你需要求出,对于 ,且满足 , 的最小值。
,,。
sol
感觉做分块顺手了许多,不到一小时。
首先看到这题类似于第四分块,于是考虑分块。
考虑对序列分块,取块长为 ,便利计算时间复杂度,后续可手动调整。
按照套路,先想区间在同一个块内的答案,块内排序,然后暴力计算即可,时间复杂度 。
如果区间不在同一个块,则分为三个部分:左散块,中多整块,右散块(都是套路了吧)。
记 表示第 块到第 块的答案, 表示 在块内的 前缀与第 块到 的上一块的答案, 表示 所在块内的 后缀与 的下一块到第 块的答案。
对于中多整块的答案,直接查询 数组,时间复杂度 。
对于左散块及右散块对中间块的答案,直接查询 数组,时间复杂度 。
对于左、右散块的内部答案,由于排序后是有序的,归并双指针计算即可,时间复杂度 。
最后考虑预处理 。
对于 ,先计算每个数与每个块的答案,然后前、后缀一下即可,时间复杂度为 (注意此时有序,使用双指针优化,时间复杂度不会达到 )。
那么 可由 递推而来,时间复杂度 。
那么总时空复杂度为 。
注意,代码中 数组是把上面的 结合成的,优化空间。
,稍逊于线段树,不过优势是可强制在线。
#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);
}
}
本文来自博客园,作者:蒟蒻orz,转载请注明原文链接:https://www.cnblogs.com/orzz/p/18122017
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】