面试的考验

一、前言

神仙题目

二、题解

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])(ijr,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

首先朴素做法的流程我们可以写成:

  1. p o s ← max ⁡ k ( k < p o s ) pos \leftarrow \max k (k < pos) posmaxk(k<pos)
  2. 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])
  3. R ← a [ p o s ] R \leftarrow a[pos] Ra[pos]
  4. 返回操作 1 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) posmaxk(k<pos,a[i]a[k]R) ( R R R最开始取 ∞ \infty )
  2. 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])
  3. R ← a [ p o s ] R \leftarrow a[pos] Ra[pos]
  4. 返回操作 1 1 1

这个表示我们不考虑 k ≤ p o s , a [ k ] ≥ a [ p o s ] k \leq pos, a[k] \geq a[pos] kpos,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] 中。

然后我们进行一个大大的优化:

  1. 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]) posmaxk(k<pos,a[i]a[k]2Ra[i]+a[i]) ( R R R最开始取 ∞ \infty )
  2. 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])
  3. R ← a [ p o s ] R \leftarrow a[pos] Ra[pos]
  4. 返回操作 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]>2Ra[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;
}
posted @ 2022-03-15 22:22  C2022lihan  阅读(16)  评论(0编辑  收藏  举报