Description
Solution
首先容易想到要把询问离线掉,按照套路枚举右端点(r),(ans_i)表示([i, r])的答案。
这样我们只需要考虑每次加进来一个点(a_r)对所有答案的贡献。
以(a_r)将点分为两种:权值大于它的和小于它的。
因为两种是对称的所以只讨论权值大于它的。
先找到最靠右的大于它的点(a_x),这个点可能不是任何区间的最优答案,但是它能更新到的区间是最多的,即左端点为(1 dots x),右端点为(r)的所有区间。
这样再在(x)左边找可以更新答案的点(a_z),它能更新到的区间是左端点为(1 dots z),右端点为(r)的所有区间,也就是说它能更新到的区间是被(a_x)能更新的区间完全包含的。所以如果新的点的权值比(a_x)还大,肯定不会更优,也就是说(a_z)必须小于(a_x)。
但是这样还是不行,严格递增序列会把这样的过程卡到单次(O(n))。
考虑之前以右端点枚举到(x)的时候,(a_z)能更新的所有区间已经被(a_x - a_z)更新过了,那么如果现在的(a_z - a_r)比(a_x - a_z)更大,就没有任何用处了。
化简这个式子,有
[a_z < frac{a_x + a_r}{2}
]
这样枚举右端点之后每次只需要找(log(n))次就行了,每次查找的复杂度是(o(logn))的,那么总复杂度是(O(n log^2 n))。
每次要用某个权值去更新一段前缀的答案,直接吉司机线段树显然非常不行。
所以考虑只对某个点更新,查询的时候查一个后缀最小值,用树状数组实现即可。
Code
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
using namespace std;
const int N = 100000;
const int INF = 0x7f7f7f7f;
int n, a[N + 50], root[N + 50], tot, cwq, b[N + 50], q, pos;
struct Ask
{
int l, r, id, ans;
} ask[N * 3 + 50];
struct Bit
{
int minn[N + 50];
void Pre()
{
memset(minn, 0x7f, sizeof(minn));
return;
}
int Lowbit(int x)
{
return x & -x;
}
void Update(int pos, int v)
{
for (int i = pos; i <= n; i += Lowbit(i))
minn[i] = min(minn[i], v);
return;
}
int Query(int pos)
{
int minnn = INF;
for (int i = pos; i; i -= Lowbit(i)) minnn = min(minnn, minn[i]);
return minnn;
}
} bit;
struct SegmentTree
{
int s[(N << 2) + 50];
void Update(int k, int l, int r, int pos, int zhi)
{
if (l == r)
{
s[k] = zhi;
return;
}
int mid = (l + r) >> 1;
if (pos <= mid) Update(k << 1, l, mid, pos, zhi);
else Update(k << 1 | 1, mid + 1, r, pos, zhi);
s[k] = max(s[k << 1], s[k << 1 | 1]);
return;
}
int Query(int k, int l, int r, int x, int y)
{
if (x > y) return 0;
if (x <= l && r <= y) return s[k];
int mid = (l + r) >> 1;
if (y <= mid) return Query(k << 1, l, mid, x, y);
else if (x > mid) return Query(k << 1 | 1, mid + 1, r, x, y);
else return max(Query(k << 1, l, mid, x, y), Query(k << 1 | 1, mid + 1, r, x, y));
}
} tr;
void Read(int &x)
{
x = 0; int p = 0; char st = getchar();
while (st < '0' || st > '9') p = (st == '-'), st = getchar();
while (st >= '0' && st <= '9') x = (x << 1) + (x << 3) + st - '0', st = getchar();
x = p ? -x : x;
return;
}
void Print(int x)
{
if (x > 9) Print(x / 10);
putchar(x % 10 + '0');
return;
}
int Cmp(Ask a, Ask b)
{
return a.r < b.r;
}
int Cmp1(Ask a, Ask b)
{
return a.id < b.id;
}
int Abs(int x)
{
return x < 0 ? -x : x;
}
int Find(int x)
{
int l = 0, r = cwq;
while (l < r)
{
int mid = (l + r + 1) >> 1;
if (b[mid] <= x) l = mid;
else r = mid - 1;
}
return l;
}
int main()
{
Read(n);
for (int i = 1; i <= n; i++) Read(a[i]), b[++cwq] = a[i];
sort(b + 1, b + cwq + 1);
cwq = unique(b + 1, b + cwq + 1) - b - 1;
for (int i = 1; i <= n; i++) a[i] = lower_bound(b + 1, b + cwq + 1, a[i]) - b;
Read(q);
for (int i = 1; i <= q; i++) Read(ask[i].l), Read(ask[i].r), ask[i].id = i;
sort(ask + 1, ask + q + 1, Cmp);
pos = 1;
bit.Pre();
for (int i = 1; i <= n; i++)
{
if (i != 1)
{
int sj = cwq, xj = a[i], tmp;
tmp = tr.Query(1, 1, cwq, xj, sj);
while (tmp)
{
bit.Update(n - tmp + 1, Abs(b[a[tmp]] - b[a[i]]));
sj = Find((b[a[tmp]] + b[a[i]]) / 2);
if (!sj) break;
if (sj >= a[tmp]) sj = a[tmp] - 1;
tmp = tr.Query(1, 1, cwq, xj, sj);
}
sj = a[i], xj = 1;
tmp = tr.Query(1, 1, cwq, xj, sj);
while (tmp)
{
bit.Update(n - tmp + 1, Abs(b[a[tmp]] - b[a[i]]));
xj = Find((b[a[tmp]] + b[a[i]]) / 2);
if (!xj) break;
if (xj <= a[tmp]) xj = a[tmp] + 1;
tmp = tr.Query(1, 1, cwq, xj, sj);
}
while (ask[pos].r == i)
{
ask[pos].ans = bit.Query(n - ask[pos].l + 1);
pos++;
}
}
tr.Update(1, 1, cwq, a[i], i);
}
sort(ask + 1, ask + q + 1, Cmp1);
for (int i = 1; i <= q; i++) Print(ask[i].ans), putchar('
');
return 0;
}