『结题记录』P2839 [国家集训队] middle
二分答案+主席树
\(Description\)
给一个长度为 \(n\) 的序列,求左端点在 \([a,b]\) 之间,右端点在 \([c,d]\) 之间的子区间中的最大中位数。
\(Solution\)
考虑二分答案。
设二分到的值为 \(x\) ,令大于 \(x\) 的数为 \(1\) ,否则为 \(-1\) 。若区间和大于等于 \(0\) ,说明答案可以再大,小于则说明要变小。
如何维护每个数的二分序列( \(-1\) , \(1\) 的序列)?可以发现,将序列排序后, \(a_i\) 的二分序列可以由 \(a_{i-1}\) 的二分序列进行部分修改(将大于 \(a_{i-1}\) 小于 \(a_i\) 的数部分修改为 \(-1\) )得出,就相当于多个历史版本,所以可以用主席树维护。
因为要求最大中位数,所以要使区间和尽可能大。对于一个左端点在 \([a,b]\) 之间,右端点在 \([c,d]\) 之间的区间。可知 \([b+1,c-1]\) 区间是一定要取的,剩下的 \([a,b]\) 和 \([c,d]\) 两个区间分别取最大后缀和与最大前缀和即可。可以参考小白逛公园。
\(Code\)
#include <iostream>
#include <algorithm>
#define int long long
const int M = 2e4+10;
int n,m,root[M],cnt;
int ans;
int qu[5];
struct node
{int num,id;}aa[M];
struct Tree
{
int ls,rs,sum,lsum,rsum;
}t[M<<5];
inline void pushUp(int q)
{
t[q].sum=t[t[q].ls].sum+t[t[q].rs].sum;
t[q].lsum=std::max(t[t[q].ls].lsum,t[t[q].ls].sum+t[t[q].rs].lsum);
t[q].rsum=std::max(t[t[q].rs].rsum,t[t[q].rs].sum+t[t[q].ls].rsum);
return ;
}
void build(int q,int l,int r)
{
if(l==r){
t[q].lsum=t[q].rsum=t[q].sum=1;
return ;
}
int mid=(l+r)>>1;
build(t[q].ls=++cnt,l,mid);
build(t[q].rs=++cnt,mid+1,r);
pushUp(q);
return ;
}
void change(int p,int q,int l,int r,int x){
if(l==r){
t[q].lsum=t[q].rsum=t[q].sum=-1;
return ;
}
t[q].ls=t[p].ls,t[q].rs=t[p].rs;
int mid = (l+r)>>1;
if(mid >= x) change(t[p].ls,t[q].ls=++cnt,l,mid,x);
else change(t[p].rs,t[q].rs=++cnt,mid+1,r,x);
pushUp(q);
return ;
}
Tree getSum(int q,int l,int r,int x,int y)
{
if(l >= x&&r <= y)
return t[q];
int mid =(l+r)>>1;
bool f1=0,f2=0;
if(mid >= x) f1=1;
if(mid < y) f2=1;
if(f1==1&&f2==0) return getSum(t[q].ls,l,mid,x,y);
else if(f1==0&&f2==1) return getSum(t[q].rs,mid+1,r,x,y);
else{
Tree lt=getSum(t[q].ls,l,mid,x,y),rt=getSum(t[q].rs,mid+1,r,x,y),nt;
nt.sum=lt.sum+rt.sum;
nt.lsum=std::max(lt.lsum,lt.sum+rt.lsum);
nt.rsum=std::max(rt.rsum,rt.sum+lt.rsum);
return nt;
}
}
inline bool check(int x,int a,int b,int c,int d)
{
int sum=0;
if(b+1<=c-1) sum+=getSum(root[x],1,n,b+1,c-1).sum;
sum+=getSum(root[x],1,n,a,b).rsum;
sum+=getSum(root[x],1,n,c,d).lsum;
if(sum<0) return 0;
else return 1;
}
int dic(int a,int b,int c,int d)
{
int l=1,r=n;
while(l<r){
int mid = (l+r)>>1;
if(check(mid,a,b,c,d)) l = mid+1;
else r=mid;
}
return l;
}
inline bool cmp(int a,int b){return a<b;}
inline bool cmp2(node a,node b){return a.num < b.num;}
inline int read(){
int num=0,fl=1;char c=getchar();
while(c<'0'||c>'9'){
if(c=='-') fl=-1;
c=getchar();
}
while(c >='0'&&c <='9'){
num=(num<<3)+(num<<1)+(c^48);
c=getchar();
}
return num*fl;
}
signed main(){
n=read();
for(int i = 1;i<=n;i++) aa[i].num=read(),aa[i].id=i;
std::sort(aa+1,aa+n+1,cmp2);
build(root[0]=++cnt,1,n);
for(int i = 1;i <= n;i++) change(root[i-1],root[i]=++cnt,1,n,aa[i].id);
m=read();
while(m--){
qu[1]=(read()+ans)%n+1,qu[2]=(read()+ans)%n+1,qu[3]=(read()+ans)%n+1,qu[4]=(read()+ans)%n+1;
std::sort(qu+1,qu+5,cmp);
ans=aa[dic(qu[1],qu[2],qu[3],qu[4])].num;
printf("%lld\n",ans);
}
return 0;
}