E. Sign on Fence
给定一个长度为 n n n的数组 a a a, 1 ≤ a i ≤ 1 0 9 1 \leq a_i \leq 10 ^ 9 1≤ai≤109,有 m m m次询问,每次给定 l , r , k l, r, k l,r,k,要我们在 [ l , r ] [l, r] [l,r]区间内找到一个长度为 k k k的区间,使得区间最小值最大,输出最大值。
考虑二分答案,如果当前二分的区间是 [ l , r ] [l, r] [l,r],我们把大于 m i d mid mid的点,在数组中都设置为 1 1 1,小于等于 m i d mid mid的点,在数组中都设置为 0 0 0,
考虑用线段树维护一个区间最长连续 1 1 1的长度,对于每组询问,我们每次查询区间 [ l i , r i ] [l_i, r_i] [li,ri]的最长连续 1 1 1长度是否大于等于 k i k_i ki即可,
由于单次查询的复杂度是 O ( n log n log n ) O(n \log n \log n) O(nlognlogn),所以可以考虑整体二分,复杂度同样也是 O ( n log n log n ) O(n \log n \log n) O(nlognlogn)的,
整体二分的时候我们先递归去处理 [ l , m i d ] [l, mid] [l,mid],这个时候不清空线段树,当我们处理 [ m i d + 1 , r ] [mid + 1, r] [mid+1,r]的时候,再清空线段树。
#include <bits/stdc++.h>
using namespace std;
const int N = 2e5 + 10;
struct Seg {
int l, r, res, len;
}a[N << 2];
Seg operator + (Seg a, Seg b) {
Seg ans;
ans.len = a.len + b.len;
ans.l = a.l == a.len ? a.l + b.l : a.l;
ans.r = b.r == b.len ? b.r + a.r : b.r;
ans.res = max({a.res, b.res, a.r + b.l});
return ans;
}
void push_up(int rt) {
a[rt] = a[rt << 1] + a[rt << 1 | 1];
}
void build(int rt, int l, int r) {
if (l == r) {
a[rt] = {0, 0, 0, 1};
return ;
}
int mid = l + r >> 1;
build(rt << 1, l, mid);
build(rt << 1 | 1, mid + 1, r);
push_up(rt);
}
void update(int rt, int l, int r, int x, int v) {
if (l == r) {
a[rt] = {v, v, v, 1};
return ;
}
int mid = l + r >> 1;
if (x <= mid) {
update(rt << 1, l, mid, x, v);
}
else {
update(rt << 1 | 1, mid + 1, r, x, v);
}
push_up(rt);
}
Seg query(int rt, int l, int r, int L, int R) {
if (l >= L && r <= R) {
return a[rt];
}
int mid = l + r >> 1;
if (L <= mid && R > mid) {
return query(rt << 1, l, mid, L, R) + query(rt << 1 | 1, mid + 1, r, L, R);
}
else if (L <= mid) {
return query(rt << 1, l, mid, L, R);
}
else {
return query(rt << 1 | 1, mid + 1, r, L, R);
}
}
struct Res {
int l, r, id, k, op;
}q[N << 1], q1[N << 1], q2[N << 1];
int b[N], ans[N], n, m, tot;
void solve(int l, int r, int L, int R) {
if (l > r || L > R) {
return ;
}
if (l == r) {
for (int i = L; i <= R; i++) {
if (q[i].op) {
ans[q[i].id] = b[l];
}
}
return ;
}
int mid = l + r >> 1, cnt1 = 0, cnt2 = 0;
for (int i = L; i <= R; i++) {
if (q[i].op) {
int cur = query(1, 1, n, q[i].l, q[i].r).res;
if (cur >= q[i].k) {
q2[++cnt2] = q[i];
}
else {
q1[++cnt1] = q[i];
}
}
else {
if (q[i].k > b[mid]) {
update(1, 1, n, q[i].id, 1);
q2[++cnt2] = q[i];
}
else {
q1[++cnt1] = q[i];
}
}
}
for (int i = 1; i <= cnt1; i++) {
q[L + i - 1] = q1[i];
}
for (int i = 1; i <= cnt2; i++) {
q[L + cnt1 + i - 1] = q2[i];
}
solve(l, mid, L, L + cnt1 - 1);
for (int i = L; i <= R; i++) {
if (!q[i].op && q[i].k > b[mid]) {
update(1, 1, n, q[i].id, 0);
}
}
solve(mid + 1, r, L + cnt1, R);
}
int main() {
// freopen("in.txt", "r", stdin);
// freopen("out.txt", "w", stdout);
scanf("%d", &n);
int cnt = 0;
for (int i = 1; i <= n; i++) {
scanf("%d", &b[i]);
q[++cnt] = {0, 0, i, b[i], 0};
}
scanf("%d", &m);
for (int i = 1, l, r, k; i <= m; i++) {
scanf("%d %d %d", &l, &r, &k);
q[++cnt] = {l, r, i, k, 1};
}
sort(b + 1, b + 1 + n);
tot = unique(b + 1, b + 1 + n) - (b + 1);
build(1, 1, n);
solve(1, tot, 1, cnt);
for (int i = 1; i <= m; i++) {
printf("%d\n", ans[i]);
}
return 0;
}