一、前言
神仙题目
二、题解
30pts30pts30pts:
考虑枚举右端点 rrr。
建立一颗答案线段树 ATreeATreeATree,线段树的 iii 号节点表示 min(a[j]−a[i])(i≤j≤r,a[j]>a[i])\min (a[j] - a[i]) (i \leq j \leq r,a[j] > a[i])min(a[j]−a[i])(i≤j≤r,a[j]>a[i])。
有一个朴素做法,就是枚举所有满足要求的 jjj, 然后更新。(甚至不需要线段树,这里加上线段树是为了拓展到 100pts100pts100pts 的做法)
时间复杂度: O(nq)/O(nqlog2(n))\mathcal{O (nq)}/\mathcal{O(nq log_2(n))}O(nq)/O(nqlog2(n))
100pts100pts100pts:
太辣鸡的朴素做法,怎么优化呢?
我们可以发现,有些一定没有用的 jjj 被我们更新了,考虑怎么跳过这些 jjj。
首先朴素做法的流程我们可以写成:
- pos←maxk(k<pos)pos \leftarrow \max k (k < pos)pos←maxk(k<pos)
- ATree[pos]←min(ATree[pos],a[pos]−a[i])ATree[pos] \leftarrow \min (ATree[pos], a[pos] - a[i])ATree[pos]←min(ATree[pos],a[pos]−a[i])
- R←a[pos]R \leftarrow a[pos]R←a[pos]
- 返回操作 111
然后我们进行一个小小的优化:
- pos←maxk(k<pos,a[i]≤a[k]≤R)pos \leftarrow \max k (k < pos, a[i] \leq a[k] \leq R)pos←maxk(k<pos,a[i]≤a[k]≤R) (RRR最开始取∞\infty∞)
- ATree[pos]←min(ATree[pos],a[pos]−a[i])ATree[pos] \leftarrow \min (ATree[pos], a[pos] - a[i])ATree[pos]←min(ATree[pos],a[pos]−a[i])
- R←a[pos]R \leftarrow a[pos]R←a[pos]
- 返回操作 111
这个表示我们不考虑 k≤pos,a[k]≥a[pos]k \leq pos, a[k] \geq a[pos]k≤pos,a[k]≥a[pos] 的点,因为 kkk 又没有 pospospos 优又不可能在 pos∈[l,r]pos \in [l, r]pos∈[l,r] 的时候处于 [l,r][l, r][l,r] 中。
然后我们进行一个大大的优化:
- pos←maxk(k<pos,a[i]≤a[k]≤⌊R−a[i]2⌋+a[i])pos \leftarrow \max k (k < pos, a[i] \leq a[k] \leq \lfloor \frac{R - a[i]}{2} \rfloor + a[i])pos←maxk(k<pos,a[i]≤a[k]≤⌊2R−a[i]⌋+a[i]) (RRR最开始取∞\infty∞)
- ATree[pos]←min(ATree[pos],a[pos]−a[i])ATree[pos] \leftarrow \min (ATree[pos], a[pos] - a[i])ATree[pos]←min(ATree[pos],a[pos]−a[i])
- R←a[pos]R \leftarrow a[pos]R←a[pos]
- 返回操作 111
考虑 k,i,posk, i, posk,i,pos (纵坐标为下标对应的 aaa, 横坐标为下标)
如果 a[k]>⌊R−a[i]2⌋+a[i]a[k] > \lfloor \frac{R - a[i]}{2} \rfloor + a[i]a[k]>⌊2R−a[i]⌋+a[i],那么我们会选择 (k,pos)(k, pos)(k,pos),那么 ATree[k]←min(ATree[k],a[k]−a[i])ATree[k] \leftarrow \min (ATree[k], a[k] - a[i])ATree[k]←min(ATree[k],a[k]−a[i]) 就不会产生贡献。
然后我们高兴的发现,操作个数不超过 log2Vlog_2Vlog2V(因为每次 [a[i],R][a[i], R][a[i],R] 这个区间都会至少减半)。
所以时间复杂度是 O(n(log2V)2)\mathcal{O (n (log_2V) ^2)}O(n(log2V)2)
//author : LH ????Who just can eat S??t
//worship WJC ????Who can f??k tourist up and down and loves ?????
//worship YJX ????Who can f??k WJC up and down
#include <cmath>
#include <queue>
#include <vector>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
#define fi first
#define se second
#define db double
//#define int long long
#define LL long long
#define ULL unsigned long long
#define PII pair <int, int>
#define MP(x,y) make_pair (x, y)
#define rep(i,j,k) for (int i = (j); i <= (k); ++i)
#define per(i,j,k) for (int i = (j); i >= (k); --i)
template <typename T> T Max (T x, T y) { return x > y ? x : y; }
template <typename T> T Min (T x, T y) { return x < y ? x : y; }
template <typename T> T Abs (T x) { return x > 0 ? x : -x; }
template <typename T>
void read (T &x) {
x = 0; T f = 1;
char ch = getchar ();
while (ch < '0' || ch > '9') {
if (ch == '-') f = -1;
ch = getchar ();
}
while (ch >= '0' && ch <= '9') {
x = (x << 3) + (x << 1) + ch - '0';
ch = getchar ();
}
x *= f;
}
template <typename T, typename... Args>
void read (T &x, Args&... args) {
read (x); read (args...);
}
char For_Print[25];
template <typename T>
void write (T x) {
if (x == 0) { putchar ('0'); return; }
if (x < 0) { putchar ('-'); x = -x; }
int poi = 0;
while (x) {
For_Print[++poi] = x % 10 + '0';
x /= 10;
}
while (poi) putchar (For_Print[poi--]);
}
template <typename T>
void print (T x, char ch) {
write (x); putchar (ch);
}
const LL Mod = 1e9 + 7;
LL square (LL x) { return (x * x) % Mod; }
void DEL (LL &x, LL y) { ((x -= y) < 0) && (x += Mod); }
void ADD (LL &x, LL y) { ((x += y) >= Mod) && (x -= Mod); }
const int Maxn = 1e5;
const int Maxm = 3 * 1e5;
const int Limit = 1e9;
const int Inf = 0x3f3f3f3f;
const int MaxSgtr = 5 * 1e6;
int n, m;
int a[Maxn + 5], ans[Maxm + 5];
struct qst {
int l, r;
}q[Maxm + 5];
vector <PII> g[Maxn + 5];
struct Tree_Array {
int BIT[Maxn + 5];
Tree_Array () { memset (BIT, 0x3f, sizeof BIT); }
void Init () { memset (BIT, 0x3f, sizeof BIT); }
int lowbit (int x) { return x & -x; }
void Update (int Index, int x) { for (int i = Index; i >= 1; i -= lowbit (i)) BIT[i] = Min (BIT[i], x); }
int Query (int Index) { int res = Inf; for (int i = Index; i <= Maxn; i += lowbit (i)) res = Min (res, BIT[i]); return res; }
}Bit;//答案树
#define mid ((l + r) >> 1)
#define ls(p) (Tr[p].ch[0])
#define rs(p) (Tr[p].ch[1])
#define _max(p) (Tr[p]._max)
struct Node {
int _max;
int ch[2];
Node () { _max = -Inf; }
};
struct Segment_Tree {
int rt, pool;
Node Tr[MaxSgtr + 5];
void Init () { rt = pool = 0; rep (i, 0, MaxSgtr + 4) Tr[i]._max = -Inf; }
int New_Node (int p) {
if (p) return p;
p = ++pool; ls (p) = 0; rs (p) = 0;
return p;
}
void Push_Up (int p) {
_max (p) = Max (_max (ls (p)), _max (rs (p)));
}
int Update (int p, int Index, int x, int l, int r) {
p = New_Node (p);
if (l == r) { _max (p) = x; return p; }
if (Index <= mid) ls (p) = Update (ls (p), Index, x, l, mid);
else rs (p) = Update (rs (p), Index, x, mid + 1, r);
Push_Up (p);
return p;
}
int Query (int p, int ql, int qr, int l, int r) {
if (ql > qr) return -Inf;
if (ql <= l && r <= qr) { return _max (p); }
int res = -Inf;
if (ql <= mid) res = Max (res, Query (ls (p), ql, qr, l, mid));
if (qr > mid) res = Max (res, Query (rs (p), ql, qr, mid + 1, r));
return res;
}
}Tree;//找 j 的辅助树
void Query () {
Bit.Init ();
Tree.Init ();
rep (i, 1, n) {
int pos, R = Limit * 2 - a[i];
while (1) {
pos = Tree.Query (Tree.rt, a[i] + 1, a[i] + ((R - a[i]) >> 1), 1, Limit);
if (pos == -Inf) break;
Bit.Update (pos, Abs (a[pos] - a[i]));
R = a[pos];
if (R == a[i]) break;
}
for (auto it : g[i])
ans[it.se] = Min (ans[it.se], Bit.Query (it.fi));
Tree.rt = Tree.Update (Tree.rt, a[i], i, 1, Limit);
}
}
signed main () {
// freopen ("D:\\lihan\\1.in", "r", stdin);
// freopen ("D:\\lihan\\1.out", "w", stdout);
memset (ans, 0x3f, sizeof ans);
read (n, m);
rep (i, 1, n) read (a[i]);
rep (i, 1, m) { read (q[i].l), read (q[i].r); g[q[i].r].push_back (MP (q[i].l, i)); }
Query ();
rep (i, 1, n) a[i] = Limit - a[i];
Query ();
rep (i, 1, m) cout << ans[i] << endl;
return 0;
}