1. 程式人生 > >題解【bzoj2653 middle】

題解【bzoj2653 middle】

scan const 位數 upd b+ turn 題解 pos 二分

Description

給你一個序列,每次詢問給出四個數 \(a,b,c,d\),求所有區間 \([l,r]\) 滿足 \(l \in [a,b], r \in [c,d]\) 的中位數的最大值。強制在線

\(n \leq 20000, Q \leq 25000,a_i \leq 10^9\)

Solution

考慮二分答案。假設現在二分出來的是 \(x\) ,那麽把 \(\ge x\) 的位置設成 \(1\)\(< x\) 的設為 \(-1\) 。那麽一個區間的中位數 \(\ge x\) 等價於這個區間的和 \(\ge 0\)

如何處理題目給的左右端點的限制?

可以發現 \([l,r]\) 必然包含 \([b+1,c-1]\)

(如果 \(b+1 \leq c+1\) 的話)所以 \([l, r]\) 的和必然包含 \([b+1, c-1]\) 的和

顯然讓 \([l,r]\) 的和最大的方案是取 \([a,b]\) 的最大右段和 和 \([c,d]\) 的最大左段和

這些都可以用線段樹維護。但這樣需要每個數都開一顆線段樹,空間爆炸。

把數組排序,這樣每個數的線段樹顯然只是由前一個數的線段樹把一個點的權值從 \(1\) 改為 \(-1\) 。可以使用主席樹的思想(貌似就是主席樹

然後就做完了。復雜度 \(O(m \log^2 n)\)

Code

#include <bits/stdc++.h>
using namespace std;
const int N = 2000; 
int n, m; int q[4]; 
struct Node {
  int d, id; 
} a[N]; 
inline bool cmp(Node x, Node y) { 
  return x.d < y.d; 
}
struct node {
  int left, right; 
  int sm, lm, rm; 
  node *ch[2]; 
  inline void upd() {
    sm = ch[0]->sm + ch[1]->sm; 
    lm = max(ch[0]->lm, ch[0]->sm + ch[1]->lm);
    rm = max(ch[1]->rm, ch[1]->sm + ch[0]->rm); 
  }
} *rt[N], pool[N * 50], *cur = pool, *ans; 
inline void B (node *r, int left, int right) { 
  r->left = left, r->right = right; 
  if(left == right) { r->sm = r->lm = r->rm = 1; return ; }
  node *lson = cur++, *rson = cur++; 
  int mid = (left + right) >> 1;
  r->ch[0] = lson, r->ch[1] = rson;
  B(lson, left, mid), B(rson, mid + 1, right);  r->upd(); 
}
inline void I (node *pre, node *now, int pos) { 
  now->left = pre->left, now->right = pre->right; 
  if(now->left == now->right) {
    now->sm = now->lm = now->rm = -1; return ; 
  } int mid = (pre->left + pre->right) >> 1; 
  if(pos <= mid) now->ch[1] = pre->ch[1], I(pre->ch[0], now->ch[0] = cur++, pos); 
  if(pos >  mid) now->ch[0] = pre->ch[0], I(pre->ch[1], now->ch[1] = cur++, pos); 
  now->upd(); 
}
inline node* Q (node *now, int l, int r) {
  if(now->left == l && now->right == r) return now; 
  if(now->ch[0]->right >= r) return Q(now->ch[0], l, r);
  else if(now->ch[1]->left <= l) return Q(now->ch[1], l, r); 
  else {
    node *ret = cur++, *L, *R;
    L = Q(now->ch[0], l, now->ch[0]->right);
    R = Q(now->ch[1], now->ch[1]->left, r);
    ret->sm = L->sm + R->sm; 
    ret->lm = max(L->lm, L->sm + R->lm);
    ret->rm = max(R->rm, R->sm + L->rm); 
    return ret; 
  }
}
inline bool check(int id) {
  int sum = 0;
  if(q[2] + 1 <= q[3] - 1) sum += Q (rt[id - 1], q[2] + 1, q[3] - 1)->sm; 
  sum += Q (rt[id - 1], q[1], q[2])->rm; 
  sum += Q (rt[id - 1], q[3], q[4])->lm; 
  return sum >= 0; 
}
int main() {
  scanf("%d", &n);
  for(int i = 1; i <= n; i++) {
    scanf("%d", &a[i].d); 
    a[i].id = i; 
  } sort(a + 1, a + n + 1, cmp);
  B(rt[0] = cur++, 1, n); 
  for(int i = 1; i <= n; i++) {
    rt[i] = cur++; I(rt[i - 1], rt[i], a[i].id);  
  } 
  int ans = 0; scanf("%d", &m); 
  for(int i = 1; i <= m; i++) {
    for(int j = 1; j <= 4; j++) {
      scanf("%d", &q[j]), 
      q[j] += ans, q[j] %= n; q[j]++; 
    }
    sort(q + 1, q + 4 + 1); 
    int l = 1, r = n; 
    while(l <= r) {
      int mid = (l + r) / 2; 
      if(check(mid)) l = mid + 1, ans = a[mid].d; 
      else r = mid - 1; 
    } printf("%d\n", ans); 
  }
  return 0; 
}

題解【bzoj2653 middle】