[bzoj 2653] middle

25 篇文章 1 订阅
12 篇文章 0 订阅

陈老师的神题,先拜一下

求中位数用二分的方法很容易想到。

可以想到我们把二分到数x后,把小于x的定义为-1,大于等于x的定义为1,只要求出一个子序列和大于等于0就说明中位数大于等于x(题目中的n/2是向上取整的)

然而之后我并不知道怎么做难过

一般我们建立主席树都是按照数组下标顺序来建的,这题比较奇葩。

假设我们只需要查询中位数为x的时候,最大的子序列和是否大于等于0,会做吧——直接弄出新数组,想gss那样搞就行了

注意到将整个数组排序之后,二分到相邻的两个答案,新数组只会有几个位置不同,就是将若干个1改为-1,不拿发现改为-1后不会再变为1了,也就是说最多总共只有n个改变值。可以利用这个建主席树!

有了这个后我们只需要询问[a,b-1]的rmax + [b,c]的sum + [c+1,d]的lmax是否大于等于0即可。

#include <vector>
#include <cstdio>
#include <cstring>
#include <algorithm>
 
using namespace std;
const int Maxn = 2000005;
int a[Maxn], b[Maxn], T[Maxn];
int son[Maxn][2], q[4];
int st,n,m,Q,i,L,R,Mid,ans;
vector <int> e[Maxn];
struct Seg_Tr
{
  int sum, lmax, rmax;
} Tree[Maxn];
 
Seg_Tr operator +(const Seg_Tr &a, const Seg_Tr &b)
{
  Seg_Tr ret;
  ret.sum = a.sum + b.sum;
  ret.lmax = max(a.lmax, a.sum+b.lmax);
  ret.rmax = max(b.rmax, b.sum+a.rmax);
  return ret;
}
 
void init(int &p,int l,int r){
  p = ++st;
  Tree[p].sum = r-l+1;
  Tree[p].lmax = r-l+1;
  Tree[p].rmax = r-l+1;
  if (l==r) return;
  int mid = (l+r)>>1;
  init(son[p][0],l,mid);
  init(son[p][1],mid+1,r);
}
 
void ins(int &p,int q,int l,int r,int k,int L,int R){
  if (L>R) {p=q; return;}
  p = ++st;
  if (l==r){
    Tree[p].sum = -1;
    Tree[p].lmax = 0;
    Tree[p].rmax = 0;
    return;
  }
  int mid = (l+r)>>1, t = L;
  while (t<=R && e[k][t]<=mid) t++;
  ins(son[p][0],son[q][0],l,mid,k,L,t-1);
  ins(son[p][1],son[q][1],mid+1,r,k,t,R);
  Tree[p] = Tree[son[p][0]] + Tree[son[p][1]];
}
 
Seg_Tr query(int p,int l,int r,int L,int R){
  if (L>R || L>r || l>R) return (Seg_Tr){0,0,0};
  if (L<=l && R>=r) return Tree[p];
  int mid = (l+r)>>1;
  return query(son[p][0],l,mid,L,R) + query(son[p][1],mid+1,r,L,R);
}
 
bool Judge(int x){
  Seg_Tr LF = query(T[x],1,n,q[0],q[1]-1);
  Seg_Tr MD = query(T[x],1,n,q[1],q[2]);
  Seg_Tr RG = query(T[x],1,n,q[2]+1,q[3]);
  return LF.rmax+MD.sum+RG.lmax >= 0;
}
 
int main(){
  scanf("%d",&n);
  for (i=1;i<=n;i++){
    scanf("%d",&a[i]);
    b[i] = a[i];
  }
  sort(b+1,b+n+1);
  m = unique(b+1,b+n+1)-b-1;
  for (i=1;i<=n;i++){
    a[i] = lower_bound(b+1,b+m+1,a[i])-b;
    e[a[i]].push_back(i);
  }
  init(T[1],1,n);
  for (i=2;i<=m;i++)
    ins(T[i],T[i-1],1,n,i-1,0,e[i-1].size()-1);
 
  scanf("%d",&Q);
  while (Q--){
    scanf("%d%d%d%d",&q[0],&q[1],&q[2],&q[3]);
    for (i=0;i<4;i++) q[i] = (q[i]+b[ans])%n+1;
    sort(q,q+4);
    L = 1; R = m;
    while (L<=R){
      int Mid = (L+R)>>1;
      if (Judge(Mid)) ans = Mid, L = Mid+1;
        else R = Mid-1;
    }
    printf("%d\n",b[ans]);
  }
  return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值