[BZOJ 2653] middle
[BZOJ 2653] middle
题意
一个长度为 \(n\) 的序列 \(a\) , 设其升序排序之后为 \(b\) , 其中位数定义为 \(b[n/2]\), 其中 \(a,b\) 从 \(0\) 开始标号,除法取下整.
给你一个长度为 \(n\) 的序列 \(s\). 回答 \(q\) 个这样的询问: \(s\) 的左端点在 \([a,b]\) 之间,右端点在 \([c,d]\) 之间的子序列中, 最大的中位数. 其中 \(a<b<c<d\). 位置也从 \(0\) 开始标号.
\(n\le 2\times 10^4,q\le2.5\times 10^4\).
强制在线.
题解
题里面最坑人的大概就是那个下取整又从 \(0\) 开始标号了...相当于取第 \(\left \lfloor \frac n 2 \right \rfloor+1\) 个值. 也就是说偶数的情况下取靠后的那个值作为中位数.
顺便在这题里了解了一些中位数处理的操作.
首先对于一个查询我们二分一个答案 \(k\), 问题转化为能否有一个合乎询问的区间中位数不小于 \(k\). 我们如果把原数列中满足 \(x\ge k\) 的看作 \(+1\), \(x<k\) 的看作 \(-1\), 那么只要有一个区间的和非负就可以满足条件.
那么我们可以用线段树来维护一下区间和以及最大前后缀和. \([b,c]\) 部分是必然要选的, 再加上 \([a,b-1]\) 的最大后缀和以及 \([c+1,d]\) 的最大前缀和即可.
至于这些线段树, 我们可以发现相邻的值对应的线段树差别不大, 只差了值相等的几个地方. 所以我们可以可持久化一下解决.
代码不难写.
参考代码
#include <bits/stdc++.h>
const int MAXN=20010;
struct Node{
struct Data{
int sum;
int lsum;
int rsum;
Data(){}
Data(int a,int b,int c):sum(a),lsum(b),rsum(c){}
Data friend operator+(const Data& a,const Data& b){
return Data(
a.sum+b.sum,
std::max(a.lsum,a.sum+b.lsum),
std::max(b.rsum,a.rsum+b.sum)
);
}
};
int l;
int r;
Data v;
Node* lch;
Node* rch;
Node(Node*);
void Print();
Node(int,int);
void Negate(int);
Data Query(int,int);
};
Node* N[MAXN];
int n;
int q;
int cnt;
int a[MAXN];
int s[MAXN];
std::vector<int> pos[MAXN];
int main(){
scanf("%d",&n);
for(int i=0;i<n;i++){
scanf("%d",a+i);
s[i]=a[i];
}
std::sort(s+1,s+n+1);
cnt=std::unique(s+1,s+n+1)-(s+1);
for(int i=0;i<n;i++){
a[i]=std::lower_bound(s+1,s+cnt+1,a[i])-s;
pos[a[i]].push_back(i);
}
N[1]=new Node(0,n-1);
for(int i=1;i<cnt;i++){
N[i+1]=new Node(N[i]);
for(auto p:pos[i])
N[i+1]->Negate(p);
}
scanf("%d",&q);
int lastans=0;
for(int i=0;i<q;i++){
int q[4];
for(int i=0;i<4;i++){
scanf("%d",q+i);
(q[i]+=lastans)%=n;
}
std::sort(q,q+4);
int l=1,r=cnt+1;
while(r-l>1){
int mid=(l+r)>>1;
int sum=0;
if(q[2]-q[1]>1)
sum+=N[mid]->Query(q[1]+1,q[2]-1).sum;
sum+=N[mid]->Query(q[0],q[1]).rsum+N[mid]->Query(q[2],q[3]).lsum;
if(sum>=0)
l=mid;
else
r=mid;
}
printf("%d\n",lastans=s[l]);
}
return 0;
}
Node::Node(Node* p){
*this=*p;
}
Node::Data Node::Query(int l,int r){
if(l<=this->l&&this->r<=r)
return this->v;
else{
if(r<=this->lch->r)
return this->lch->Query(l,r);
if(this->rch->l<=l)
return this->rch->Query(l,r);
return this->lch->Query(l,r)+this->rch->Query(l,r);
}
}
void Node::Negate(int x){
if(this->l==this->r)
this->v=Data(-1,-1,-1);
else{
if(x<=this->lch->r){
this->lch=new Node(this->lch);
this->lch->Negate(x);
}
else{
this->rch=new Node(this->rch);
this->rch->Negate(x);
}
this->v=this->lch->v+this->rch->v;
}
}
void Node::Print(){
if(this==NULL)
return;
printf("[%d,%d] {sum=%d,lsum=%d,rsum=%d}\n",l,r,v.sum,v.lsum,v.rsum);
this->lch->Print();
this->rch->Print();
}
Node::Node(int l,int r):l(l),r(r){
if(l==r)
this->v=Data(1,1,1);
else{
int mid=(l+r)>>1;
this->lch=new Node(l,mid);
this->rch=new Node(mid+1,r);
this->v=this->lch->v+this->rch->v;
}
}
本博客已弃用, 新个人主页: https://rvalue.moe, 新博客: https://blog.rvalue.moe