1. 程式人生 > 其它 >【題解】P2839 [國家集訓隊]middle

【題解】P2839 [國家集訓隊]middle

描述:

一個長度為 \(n\) 的序列 \(a\),設其排過序之後為 \(b\),其中位數定義為 \(b_{n/2}\),其中 \(a,b\)\(0\) 開始標號,除法取下整。

給你一個長度為 \(n\) 的序列 \(s\)

回答 \(Q\) 個這樣的詢問:\(s\) 的左端點在 \([a,b]\) 之間,右端點在 \([c,d]\) 之間的子區間中,最大的中位數。

\(n \leq 20000\)\(Q \leq 25000\)。強制線上。

思路:

看到求 最大 的中位數,想到二分。

二分 \(mid\) ,將大於等於 \(mid\) 的數的值設為 \(1\) ,小於 \(mid\) 的數的值設為 \(0\)

那麼當區間和為 \(0\) 時,\(mid\) 為區間的中位數。

二分的時候,區間和 \(\ge 0\) ,那麼中位數應該比 \(mid\) 大,反之,則更小。

那麼我們應該要區間儘量大。

考慮暴力,對於每個二分的值,預處理出它對應的序列的線段樹。

當我們發現建立線段樹的時間複雜度或空間複雜度過高時,考慮利用原有資訊,也就是主席樹。(如:對每個量建立一顆線段樹)

那麼可以將值排序,發現每次改動只會將一個位置的 \(1\) 變為 \(-1\)

時間複雜度 \(O(n\log^2 n)\)

程式碼:

#include <bits/stdc++.h>

#define LL long long
#define ULL unsigned long long
#define DB double
#define PR pair <int, int>
#define MK make_pair
#define pb push_back
#define fi first
#define se second
#define RI register int
#define Low(x) (x & (-x))

using namespace std;

const int kN = 3e4 + 5;

int n, A[kN], rt[kN], cnt, Q; PR B[kN];

struct Node {
  int siz, lm, rm;
};

struct Smt {
  Node d[kN * 40]; int ls[kN * 40], rs[kN * 40];
#define ls(p) ls[p]
#define rs(p) rs[p]

  Node Merge(Node x, Node y) {
    Node res;
    res.siz = x.siz + y.siz;
    res.lm = max(x.lm, x.siz + y.lm);
    res.rm = max(y.rm, y.siz + x.rm);
    return res;
  }
  
  void Build(int l, int r, int &p) {
    p = ++cnt;
    if (l == r) {d[p] = {1, 1, 1}; return;}
    int mid = (l + r) >> 1;
    Build(l, mid, ls(p)), Build(mid + 1, r, rs(p));
    d[p] = Merge(d[ls(p)], d[rs(p)]);
  }
  
  void Modify(int l, int r, int pos, int k, int &p1, int &p2) {
    p2 = ++cnt;
    ls(p2) = ls(p1), rs(p2) = rs(p1); d[p2] = d[p1];
    if (l == r) {d[p2].siz += k, d[p2].lm = d[p2].rm = d[p2].siz; return;}
    int mid = (l + r) >> 1;
    if (pos <= mid) Modify(l, mid, pos, k, ls(p1), ls(p2));
    else Modify(mid + 1, r, pos, k, rs(p1), rs(p2));
    d[p2] = Merge(d[ls(p2)], d[rs(p2)]);
  }

  Node Query(int l, int r, int L, int R, int p) {
    if (L > R) return (Node) {0, 0, 0};
    if (l >= L && r <= R) return d[p];
    int mid = (l + r) >> 1;
    if (L <= mid && R > mid)
      return Merge(Query(l, mid, L, R, ls(p)), Query(mid + 1, r, L, R, rs(p)));
    if (L <= mid)
      return Query(l, mid, L, R, ls(p));
    return Query(mid + 1, r, L, R, rs(p));
  }
  
} T;

signed main() {
  scanf("%d", &n);
  for (int i = 1; i <= n; ++i) scanf("%d", &A[i]), B[i] = MK(A[i], i);
  sort(B + 1, B + 1 + n);
  T.Build(1, n, rt[1]);
  for (int i = 2; i <= n; ++i) {
    T.Modify(1, n, B[i - 1].se, -2, rt[i - 1], rt[i]);
  }
  int las = 0;
  scanf("%d", &Q);
  while (Q--) {
    int a, b, c, d; scanf("%d%d%d%d", &a, &b, &c, &d);
    int q[4] = {(a + las) % n, (b + las) % n, (c + las) % n, (d + las) % n};
    sort(q, q + 4);
    a = q[0] + 1, b = q[1] + 1, c = q[2] + 1, d = q[3] + 1;
    //    cout << a << ' ' << b << ' ' << c << ' ' << d << endl;
    int l = 1, r = n, ans = 0;
    while (l <= r) {
      int mid = (l + r) >> 1;
      Node x = T.Query(1, n, a, b, rt[mid]), y = T.Query(1, n, b + 1, c - 1, rt[mid]),
	z = T.Query(1, n, c, d, rt[mid]);
      int res = x.rm + y.siz + z.lm;
      if (res >= 0) ans = mid, l = mid + 1;
      else r = mid - 1;
    } printf("%d\n", las = B[ans].fi);
  }
  return 0;
}

Undersea Palace