Solution
首先将询问离线,按照右端点从小到大排序。假设我们已经知道了 ([l,r-1]) 的答案,现在将 (a_r) 加入更新答案。显然只需要考虑 (a_r) 对答案的贡献。同时建立线段树,(seg_i) 维护 (i) 位置到 (r) 位置的答案。
(a_r) 贡献答案只有 (2) 种情况:前面存在一个数 (a_j > a_r),则用 (a_j - a_r) 对 (seg_{1-j}) 进行更新;前面存在一个数 (a_j < a_r),则用 (a_r - a_j) 对 (seg_{1-j}) 进行更新。
因为这两种情况本质相同,所以我们只讲解其中的第一种情况。考虑用一种最暴力的办法:对于每个 (r),找到其前面第一个大于它的数,更新答案;继续找第二个...很显然这样做会 T 到飞起。那么这种暴力可以优化吗?答案是可以的,使用主席树维护权值区间的最大位置。每次使用主席树查找上一个比它大的数。
但是这样还不够,需要优化。如果 (a_x>a_r,a_z>a_r(z<x<r)),那么如果 (a_x-a_z<a_z-a_r),(a_z-a_r) 将不会对答案产生任何贡献。
所以我们就找到了权值的一个边界 (frac{a_r+a_x}{2})。这样每次除下去,复杂度会变成 (log)。
Code
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define int long long
using namespace std;
const int N = 333333, INF = 1e18;
struct que { int l, r, id; } q[N];
int n, m, pos, len, now = 1, a[N], b[N];
int minn[N << 5], tag[N << 5], hav[N << 5], maxx[N << 5], L[N << 5], R[N << 5], t[N], ans[N], cnt = 0, last = 0;
struct SegmentTree
{
void push_up(int x) { minn[x] = min(min(minn[x], minn[x * 2]), minn[x * 2 + 1]); }
void push_down(int x)
{
tag[x * 2] = min(tag[x * 2], tag[x]);
tag[x * 2 + 1] = min(tag[x * 2 + 1], tag[x]);
minn[x] = min(minn[x], tag[x]);
minn[x * 2] = min(minn[x * 2], tag[x * 2]);
minn[x * 2 + 1] = min(minn[x * 2 + 1], tag[x * 2 + 1]);
}
void update(int x, int l, int r, int stdl, int stdr, int k)
{
if(l > stdr || r < stdl) return ;
if(stdl <= l && stdr >= r)
{
tag[x] = min(tag[x], k);
minn[x] = min(minn[x], tag[x]);
push_down(x);
return ;
}
int mid = (l + r) >> 1;
push_down(x);
update(x * 2, l, mid, stdl, stdr, k);
update(x * 2 + 1, mid + 1, r, stdl, stdr, k);
push_up(x);
}
int query(int x, int l, int r, int stdl, int stdr)
{
if(l > stdr || r < stdl) return INF;
if(stdl <= l && stdr >= r) return minn[x];
int mid = (l + r) >> 1;
push_down(x);
return min(query(x * 2, l, mid, stdl, stdr), query(x * 2 + 1, mid + 1, r, stdl, stdr));
push_up(x);
}
} Tree1;
struct ChairmanTree
{
int build(int l, int r)
{
int root = ++cnt;
if(l < r)
{
int mid = (l + r) >> 1;
L[root] = build(l, mid);
R[root] = build(mid + 1, r);
}
return root;
}
int update(int pre, int l, int r, int x, int k)
{
int root = ++cnt;
L[root] = L[pre], R[root] = R[pre];
maxx[root] = max(maxx[pre], k);
if(l < r)
{
int mid = (l + r) >> 1;
if(x <= mid) L[root] = update(L[pre], l, mid, x, k);
else R[root] = update(R[pre], mid + 1, r, x, k);
}
return root;
}
int query(int x, int l, int r, int stdl, int stdr)
{
if(b[l] > stdr || b[r] < stdl) return 0;
if(stdl <= b[l] && stdr >= b[r]) return maxx[x];
int mid = (l + r) >> 1;
return max(query(L[x], l, mid, stdl, stdr), query(R[x], mid + 1, r, stdl, stdr));
}
} Tree2;
bool cmp(que a, que b) { return a.r < b.r; }
signed main()
{
scanf("%lld", &n);
memset(hav, 0, sizeof(hav));
memset(maxx, 0, sizeof(maxx));
for(int i = 0; i < N << 5; i++) tag[i] = minn[i] = 1e18;
for(int i = 1; i <= n; i++) scanf("%lld", &a[i]), b[i] = a[i];
scanf("%lld", &m);
for(int i = 1; i <= m; i++) scanf("%lld%lld", &q[i].l, &q[i].r), q[i].id = i;
sort(q + 1, q + m + 1, cmp);
sort(b + 1, b + n + 1);
t[0] = Tree2.build(1, n);
b[0] = 0;
for(int i = 1; i <= n; i++) a[i] = lower_bound(b + 1, b + n + 1, a[i]) - b;
for(int i = 1; i <= n; i++)
{
pos = 0;
pos = Tree2.query(t[i - 1], 1, n, 0, b[a[i]]);
if(pos != 0) Tree1.update(1, 1, n, 1, pos, b[a[i]] - b[a[pos]]);
while(pos)
{
pos = Tree2.query(t[pos - 1], 1, n, (b[a[pos]] + b[a[i]]) / 2 + 1, b[a[i]]);
if(pos <= 0) break;
Tree1.update(1, 1, n, 1, pos, b[a[i]] - b[a[pos]]);
}
pos = 0;
pos = Tree2.query(t[i - 1], 1, n, b[a[i]], b[n]);
if(pos != 0) Tree1.update(1, 1, n, 1, pos, b[a[pos]] - b[a[i]]);
while(pos)
{
if((b[a[pos]] + b[a[i]]) % 2 == 0) len = (b[a[pos]] + b[a[i]]) / 2 - 1;
else len = (b[a[pos]] + b[a[i]]) / 2;
pos = Tree2.query(t[pos - 1], 1, n, b[a[i]], len);
if(pos <= 0) break;
Tree1.update(1, 1, n, 1, pos, b[a[pos]] - b[a[i]]);
}
while(q[now].r <= i && now <= m)
{
if(q[now].r == i) ans[q[now].id] = Tree1.query(1, 1, n, q[now].l, q[now].r);
now++;
}
t[i] = Tree2.update(t[i - 1], 1, n, a[i], i);
}
for(int i = 1; i <= m; i++) printf("%lld
", ans[i]);
return 0;
}