#include <bits/stdc++.h>
using namespace std;
// Giải thuật offline để trả về hiệu tuyệt đối nhỏ nhất giữa hai số trong mỗi truy vấn trên mảng con
// Sử dụng kỹ thuật Mo’s algorithm cải tiến với phân mảnh theo block và linked-list động
struct Query {
int left; // vị trí trái của truy vấn
int right; // vị trí phải của truy vấn
int index; // chỉ số truy vấn để trả kết quả sau
};
// Hằng số
constexpr int MAXN = 300000 + 5;
constexpr int BLOCK_SIZE = 400;
constexpr int INF = 1e9;
// Mảng dữ liệu chính
int nextPos[MAXN]; // nextPos[i]: chỉ số phần tử tiếp theo trong liên kết
int prevPos[MAXN]; // prevPos[i]: chỉ số phần tử trước đó trong liên kết
int compressed[MAXN]; // giá trị mảng đã nén tọa độ (với trùng lặp xử lý offset)
int original[MAXN]; // giá trị gốc ban đầu (dùng cho tính toán độ khác nhau)
int answer[MAXN]; // kết quả cho mỗi truy vấn
int dp[BLOCK_SIZE + 5][MAXN]; // bảng dp tiền tính cho các đoạn ngắn (độ dài <= BLOCK_SIZE)
int blockMin[MAXN]; // giá trị tối ưu khi xử lý theo block
map<int,int> freqMap; // hỗ trợ đếm số lần trùng giá trị trong nén tọa độ
vector<Query> queriesByBlock[MAXN / BLOCK_SIZE + 5]; // nhóm truy vấn theo block của trái
// So sánh để sắp xếp truy vấn giảm dần theo right
bool compareByRightDesc(const Query &a, const Query &b) {
return a.right > b.right;
}
// Thêm vị trí x vào tập đang xét (linked list động), trả về hiệu nhỏ nhất với láng giềng
int insertPosition(int x) {
int best = INF;
// Kiểm tra phần tử trước
if (prevPos[x] >= 1) {
best = min(best, original[x] - original[prevPos[x]]);
}
// Kiểm tra phần tử sau
if (nextPos[x] <= MAXN - 1) {
best = min(best, original[nextPos[x]] - original[x]);
}
// Cập nhật liên kết
nextPos[prevPos[x]] = x;
prevPos[nextPos[x]] = x;
return best;
}
// Xóa vị trí x khỏi tập đang xét
void removePosition(int x) {
nextPos[prevPos[x]] = nextPos[x];
prevPos[nextPos[x]] = prevPos[x];
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n;
cin >> n; // đọc số phần tử mảng
// Đọc và lưu giá trị ban đầu
for (int i = 1; i <= n; ++i) {
cin >> compressed[i];
original[i] = compressed[i];
}
// Nén tọa độ: sort và gán lại chỉ số
sort(original + 1, original + n + 1);
original[0] = -INF;
original[n + 1] = INF;
for (int i = 1; i <= n; ++i) {
int rank = lower_bound(original + 1, original + n + 1, compressed[i]) - original;
compressed[i] = rank + (freqMap[rank]++);
}
int q;
cin >> q; // số truy vấn
// Nhóm các truy vấn theo block của chỉ số trái
for (int i = 1; i <= q; ++i) {
int l, r;
cin >> l >> r;
int blockId = (l - 1) / BLOCK_SIZE;
queriesByBlock[blockId].push_back({l, r, i});
}
// Sắp xếp truy vấn trong mỗi block giảm dần theo right
int numBlocks = (n + BLOCK_SIZE - 1) / BLOCK_SIZE;
for (int b = 0; b < numBlocks; ++b) {
sort(queriesByBlock[b].begin(), queriesByBlock[b].end(), compareByRightDesc);
}
// Tiền tính dp cho các đoạn ngắn độ dài <= BLOCK_SIZE
for (int len = 1; len <= min(BLOCK_SIZE, n); ++len) {
for (int start = 1; start + len - 1 <= n; ++start) {
if (len == 1) {
dp[len][start] = INF; // chỉ một phần tử, không có cặp
} else {
dp[len][start] = min({
dp[len - 1][start],
dp[len - 1][start + 1],
abs(compressed[start] - compressed[start + len - 1])
});
}
}
}
// Xử lý từng block
for (int b = 0; b < numBlocks; ++b) {
int leftBound = b * BLOCK_SIZE + 1;
int rightBound = min(n, (b + 1) * BLOCK_SIZE);
// Khởi tạo linked list đầy đủ 0..n+1
for (int i = 0; i <= n + 1; ++i) {
prevPos[i] = i - 1;
nextPos[i] = i + 1;
}
// Loại hết phần tử ngoài [1..rightBound]
for (int i = 1; i < leftBound; ++i) removePosition(compressed[i]);
for (int i = rightBound + 1; i <= n; ++i) removePosition(compressed[i]);
// Tính blockMin: hiệu nhỏ nhất từ rightBound tới n
int currentMin = INF;
for (int i = rightBound + 1; i <= n; ++i) {
currentMin = min(currentMin, insertPosition(compressed[i]));
blockMin[i] = currentMin;
}
// Duyệt các truy vấn trong block b
int ptr = n;
for (auto &query : queriesByBlock[b]) {
int l = query.left;
int r = query.right;
// Nếu độ dài nhỏ, dùng dp sẵn
if (r - l + 1 <= BLOCK_SIZE) {
answer[query.index] = dp[r - l + 1][l];
continue;
}
// Bắt đầu từ kết quả tiền tính
int res = blockMin[r];
// Thu nhỏ ptr lại đúng r
while (ptr > r) {
removePosition(compressed[ptr--]);
}
// Loại các phần tử trong [leftBound..rightBound-1]
for (int i = leftBound; i < rightBound; ++i) removePosition(compressed[i]);
// Thêm các phần tử từ r-1 xuống l và cập nhật res
int tmpMin = res;
for (int i = rightBound - 1; i >= l; --i) {
tmpMin = min(tmpMin, insertPosition(compressed[i]));
}
res = tmpMin;
// Khôi phục lại các phần tử đã xóa tạm
for (int i = l - 1; i >= leftBound; --i) insertPosition(compressed[i]);
answer[query.index] = res;
}
}
// In kết quả cho từng truy vấn theo thứ tự ban đầu
for (int i = 1; i <= q; ++i) {
cout << answer[i] << "\n";
}
return 0;
}