码迷,mamicode.com
首页 > 其他好文 > 详细

题解【bzoj2653 middle】

时间:2019-01-20 14:59:39      阅读:184      评论:0      收藏:0      [点我收藏+]

标签:void   inline   如何   log   solution   namespace   nod   端点   return   

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】

标签:void   inline   如何   log   solution   namespace   nod   端点   return   

原文地址:https://www.cnblogs.com/acfunction/p/10294753.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!