面试的考验
一、前言
神仙题目
二、题解
30 p t s 30pts 30pts:
考虑枚举右端点 r r r。
建立一颗答案线段树 A T r e e ATree ATree,线段树的 i i i 号节点表示 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])。
有一个朴素做法,就是枚举所有满足要求的 j j j, 然后更新。(甚至不需要线段树,这里加上线段树是为了拓展到 100 p t s 100pts 100pts 的做法)
时间复杂度: O ( n q ) / O ( n q l o g 2 ( n ) ) \mathcal{O (nq)}/\mathcal{O(nq log_2(n))} O(nq)/O(nqlog2(n))
100 p t s 100pts 100pts:
太辣鸡的朴素做法,怎么优化呢?
我们可以发现,有些一定没有用的 j j j 被我们更新了,考虑怎么跳过这些 j j j。
首先朴素做法的流程我们可以写成:
- p o s ← max k ( k < p o s ) pos \leftarrow \max k (k < pos) pos←maxk(k<pos)
- A T r e e [ p o s ] ← min ( A T r e e [ p o s ] , a [ p o s ] − a [ i ] ) ATree[pos] \leftarrow \min (ATree[pos], a[pos] - a[i]) ATree[pos]←min(ATree[pos],a[pos]−a[i])
- R ← a [ p o s ] R \leftarrow a[pos] R←a[pos]
- 返回操作 1 1 1
然后我们进行一个小小的优化:
- p o s ← max k ( k < p o s , 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) ( R R R最开始取 ∞ \infty ∞)
- A T r e e [ p o s ] ← min ( A T r e e [ p o s ] , a [ p o s ] − a [ i ] ) ATree[pos] \leftarrow \min (ATree[pos], a[pos] - a[i]) ATree[pos]←min(ATree[pos],a[pos]−a[i])
- R ← a [ p o s ] R \leftarrow a[pos] R←a[pos]
- 返回操作 1 1 1
这个表示我们不考虑 k ≤ p o s , a [ k ] ≥ a [ p o s ] k \leq pos, a[k] \geq a[pos] k≤pos,a[k]≥a[pos] 的点,因为 k k k 又没有 p o s pos pos 优又不可能在 p o s ∈ [ l , r ] pos \in [l, r] pos∈[l,r] 的时候处于 [ l , r ] [l, r] [l,r] 中。
然后我们进行一个大大的优化:
- p o s ← max k ( k < p o s , 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]) ( R R R最开始取 ∞ \infty ∞)
- A T r e e [ p o s ] ← min ( A T r e e [ p o s ] , a [ p o s ] − a [ i ] ) ATree[pos] \leftarrow \min (ATree[pos], a[pos] - a[i]) ATree[pos]←min(ATree[pos],a[pos]−a[i])
- R ← a [ p o s ] R \leftarrow a[pos] R←a[pos]
- 返回操作 1 1 1
考虑 k , i , p o s k, i, pos k,i,pos (纵坐标为下标对应的 a a a, 横坐标为下标)
如果 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 , p o s ) (k, pos) (k,pos),那么 A T r e e [ k ] ← min ( A T r e e [ k ] , a [ k ] − a [ i ] ) ATree[k] \leftarrow \min (ATree[k], a[k] - a[i]) ATree[k]←min(ATree[k],a[k]−a[i]) 就不会产生贡献。
然后我们高兴的发现,操作个数不超过 l o g 2 V log_2V log2V(因为每次 [ a [ i ] , R ] [a[i], R] [a[i],R] 这个区间都会至少减半)。
所以时间复杂度是 O ( n ( l o g 2 V ) 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;
}