P5926 [JSOI2009] 面试的考验
lxl 上课讲的题,来写个题解。
双倍经验:CF765F。
三倍经验:CF1793F。
给出序列
,有 次询问,每次询问给出 ,求两个数 满足 且 最小。输出这个最小值。
。设值域为 , 。
不妨令
一个朴素的想法是,把所有的点对
但是真的所有
简单理解一下,一个点对能够支配另一个点对,当且仅当这个点对的限制更宽松,且权值不超过另一个点对。
考虑把所有有贡献的点对
-
,此时权值 。设当前找到
是有贡献的点对,考虑怎样的 (满足 )仍然有贡献。首先 ,不然 会支配 。其次,
,不然 会支配 。此时我们将不等式变形,会发现 ,即权值至少减半。因此以 为左点的有贡献点对不会超过 个。因为找到
个点对后,权值已经变成 ,由于点对的权值是绝对值,显然不可能再找到一个点对,使得它的值小于 。此时,
。 -
的情况类似分析即可。
这么一来,需要保留的点对就只有
为了配合上述过程找出有贡献点对,我们需要支持这个操作:
查询一个最小的位置
,使得 且 。
没懂这一步大佬们是如何不可持久化做的,只能来介绍一下我的憨憨做法。建立一棵可持久化权值线段树,版本
设为未出现的权值设为无穷大的意义就体现出来了,我要查询的是出现过的数的最小位置,没出现过的数因为权值无穷大,会在取最小值时被舍去。若查询结果为最小值,则不存在这样的数,说明不能找到更多的点对了。
把所有有贡献的点对保留后,离线询问,将第一维降序排序(修改在前),用权值树状数组维护第二维的限制。
时间复杂度为
#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;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】