题目链接:http://codeforces.com/contest/484/problem/E
题目大意:给定一个长度为n的序列,以及Q个询问 (l, r, w) , 每个询问输出 max{ min(a[i], a[i + 1], ..., a[i + w - 1]), i >= l, i + w - 1 <= r}
基本思路:考虑数字从大到小加入序列,对于第i个询问(li,ri,wi)。如果答案ans是合法的,则加到数字ans时,区间[li, ri]中必然有连续一段长度大于等于w的数字已经被加入。如果我们只需要验证询问的答案是否都大于等于ans,则我们可以把大于等于ans的数全部加入序列,然后用线段树就可以很好地处理。
对于原问题,我们只需要二分答案,然后利用可持久化线段树即可。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 1e5 + 5, M = 8e6 + 5;
pair<int, int> a[N];
int ll[M], rr[M], root[N];
int leftMax[M], rightMax[M], allMax[M];
int tot;
int cmp(const pair<int, int> &a, const pair<int, int> &b) {
return a > b;
}
void newpoint(int pre, int &x) {
x = ++tot;
ll[x] = ll[pre];
rr[x] = rr[pre];
leftMax[x] = leftMax[pre];
rightMax[x] = rightMax[pre];
allMax[x] = allMax[pre];
}
void add(int prev, int &k, int m, int n, int c) {
newpoint(prev, k);
if (m > c || n < c) return ;
if (m == n) {
leftMax[k] = rightMax[k] = allMax[k] = 1;
return ;
}
int mid = m + n >> 1;
int &z1 = ll[k], &z2 = rr[k];
add(ll[prev], z1, m, mid, c);
add(rr[prev], z2, mid + 1, n, c);
if (leftMax[z1] == mid - m + 1) leftMax[k] = leftMax[z1] + leftMax[z2];
else leftMax[k] = leftMax[z1];
if (rightMax[z2] == n - mid) rightMax[k] = rightMax[z2] + rightMax[z1];
else rightMax[k] = rightMax[z2];
allMax[k] = max(allMax[z1], allMax[z2]);
allMax[k] = max(allMax[k], rightMax[z1] + leftMax[z2]);
}
int find(int k, int m, int n, int l, int r, int &L) {
if (m > r || n < l || k == 0) return 0;
if (m >= l && n <= r) {
int t = max(L + leftMax[k], allMax[k]);
if (allMax[k] == n - m + 1) L += n - m + 1;
else L = rightMax[k];
return t;
}
int mid = m + n >> 1;
int t1 = find(ll[k], m, mid, l, r, L);
int t2 = find(rr[k], mid + 1, n, l, r, L);
return max(t1, t2);
}
int main() {
// freopen("1.txt", "r", stdin);
int n;
scanf("%d", &n);
for (int i = 1; i <= n; ++i) {
scanf("%d", &a[i].first);
a[i].second = i;
}
sort(a + 1, a + n + 1, cmp);
for (int i = 1; i <= n; ++i) {
int c = a[i].second;
add(root[i - 1], root[i], 1, n, c);
}
int Q;
scanf("%d", &Q);
while (Q--) {
int l, r, w;
scanf("%d%d%d", &l, &r, &w);
int head = 1, tail = n;
int L;
while (head + 1 < tail) {
int mid = head + tail >> 1;
L = 0;
if (find(root[mid], 1, n, l, r, L) >= w)
tail = mid;
else head = mid;
}
L = 0;
if (find(root[head], 1, n, l, r, L) >= w)
tail = head;
L = 0;
printf("%d\n", a[tail].first);
}
return 0;
}