P2839 [国家集训队]middle(二分 套 主席树)

P2839 [国家集训队]middle

有一个长度为 n n n的序列,有 m m m次询问,每次询问 a , b , c , d a, b, c, d a,b,c,d,为 l ∈ [ a , b ] , r ∈ [ c , d ] l \in [a, b], r \in [c, d] l[a,b],r[c,d] [ l , r ] [l, r] [l,r]区间的中位数最大是多少,强制在线, 1 ≤ n ≤ 20000 , 1 ≤ m ≤ 25000 1 \leq n \leq 20000, 1 \leq m \leq 25000 1n20000,1m25000

由于有 a ≤ b ≤ c ≤ d a \leq b \leq c \leq d abcd,分三个区间讨论 [ a , b ] , [ b + 1 , c − 1 ] , [ c , d ] [a, b], [b + 1, c - 1], [c, d] [a,b],[b+1,c1],[c,d],其中 [ b + 1 , c − 1 ] [b + 1, c - 1] [b+1,c1]这个区间是必选的, [ a , b ] , [ c , d ] [a, b], [c, d] [a,b],[c,d]这俩个区间可选可不选,

我们考虑二分答案,对于 m i d ≤ a i mid \leq a_i midai的我们设置为 1 1 1,否则我们设置为 − 1 -1 1,对于选定的区间,如果中位数是等于 m i d mid mid的,则一定有区间和 > = 0 >= 0 >=0

由于我们要使区间中位数尽可能地大,所以我们定然是在 [ a , b ] [a, b] [a,b]上选一个点 p p p 1 , − 1 1, -1 1,1序列上使得 [ p , b ] [p, b] [p,b]的和最大,在 [ c , d ] [c, d] [c,d]上选定一个点 q q q也是如此。

如此做的话,单次询问复杂度将会是 O ( n log ⁡ n log ⁡ n ) O(n \log n \log n ) O(nlognlogn)的,有没有办法高效来完成呢,整体二分似乎是一个比较好的方法,但是这题要求在线求解,,,

整理一下上面我们所需要的值, [ b + 1 , c − 1 ] [b + 1, c - 1] [b+1,c1]的区间和, [ a , b ] [a, b] [a,b]区间的后缀最大值, [ c , d ] [c, d] [c,d]区间的前缀最大值,

对于任意一个区间的后缀最大值,前缀最大值都可用线段树维护,同样的区间和也可以用线段树维护,

目前我们所需的就是对于任何一个 m i d mid mid,我们要精确地知道对于某段区间地前缀最大,前缀最小,区间和,

这里就可以考虑用主席树,对每个二分地 m i d mid mid都建立一颗主席树,对于每次二分地 m i d mid mid是否合法,我们只要到对应地主席树上查找即可。

#include <bits/stdc++.h>

using namespace std;

const int N = 2e4 + 10;

struct Seg {
  int l, r, sum;
}t[N << 6];

int root[N], ls[N << 6], rs[N << 6], num;

int a[N], b[N], n, m;

Seg operator + (Seg a, Seg b) {
  Seg ans;
  ans.sum = a.sum + b.sum;
  ans.l = max(a.l, a.sum + b.l);
  ans.r = max(b.r, b.sum + a.r);
  return ans;
}

void push_up(int rt) {
  t[rt] = t[ls[rt]] + t[rs[rt]];
}

void build(int &rt, int l, int r) {
  rt = ++num;
  if (l == r) {
    t[rt] = {-1, -1, -1};
    return ;
  }
  int mid = l + r >> 1;
  build(ls[rt], l, mid);
  build(rs[rt], mid + 1, r);
  push_up(rt);
}

void update(int &rt, int pre, int l, int r, int x) {
  rt = ++num, ls[rt] = ls[pre], rs[rt] = rs[pre];
  if (l == r) {
    t[rt] = {1, 1, 1};
    return ;
  }
  int mid = l + r >> 1;
  if (x <= mid) {
    update(ls[rt], ls[pre], l, mid, x);
  }
  else {
    update(rs[rt], rs[pre], mid + 1, r, x);
  }
  push_up(rt);
}

Seg query(int rt, int l, int r, int L, int R) {
  if (l >= L && r <= R) {
    return t[rt];
  }
  int mid = l + r >> 1;
  if (L <= mid && R > mid) {
    return query(ls[rt], l, mid, L, R) + query(rs[rt], mid + 1, r, L, R);
  }
  else if (L <= mid) {
    return query(ls[rt], l, mid, L, R);
  }
  else {
    return query(rs[rt], mid + 1, r, L, R);
  }
}

int aa, bb, cc, dd;

bool judge(int x) {
  int res = 0;
  res += query(root[x], 1, n, aa, bb).r;
  if (bb + 1 <= cc - 1) {
    res += query(root[x], 1, n, bb + 1, cc - 1).sum;
  }
  res += query(root[x], 1, n, cc, dd).l;
  return res >= 0;
}

int main() {
  // freopen("in.txt", "r", stdin);
  // freopen("out.txt", "w", stdout);
  scanf("%d", &n);
  for (int i = 1; i <= n; i++) {
    scanf("%d", &a[i]);
    b[i] = i;
  }
  scanf("%d", &m);
  build(root[n + 1], 1, n);
  sort(b + 1, b + 1 + n, [&] (int x, int y) {
    return a[x] < a[y];
  });
  for (int i = n; i >= 1; i--) {
    update(root[i], root[i + 1], 1, n, b[i]);
  }
  int ans = 0;
  for (int i = 1; i <= m; i++) {
    scanf("%d %d %d %d", &aa, &bb, &cc, &dd);
    aa = (aa + ans) % n + 1, bb = (bb + ans) % n + 1;
    cc = (cc + ans) % n + 1, dd = (dd + ans) % n + 1;
    int tmp[5] = {0, aa, bb, cc, dd};
		sort(tmp + 1, tmp + 4 + 1);
		aa = tmp[1], bb = tmp[2], cc = tmp[3], dd = tmp[4];
    int l = 1, r = n;
    while (l < r) {
      int mid = l + r + 1 >> 1;
      if (judge(mid)) {
        l = mid;
      }
      else {
        r = mid - 1;
      }
    }
    ans = a[b[l]];
    printf("%d\n", ans);
  }
  return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值