E. Sign on Fence(整体二分 + 线段树维护区间最大连续 1 的个数)

E. Sign on Fence

给定一个长度为 n n n的数组 a a a 1 ≤ a i ≤ 1 0 9 1 \leq a_i \leq 10 ^ 9 1ai109,有 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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值