( ext{Description})
( ext{Solution})
首先如果让你求 ([l,r]) 区间的中位数是很好算的:具体就是搞一棵主席树,然后二分中位数并算出它在区间的排名用于检验。
可这道题是不定区间,我们二分的中位数实际上算不出排名。但题目需要最大化中位数:也就是说如果我们二分一个 (mid),我们选择的区间需要使大于 (mid) 的值尽量多,这样就越可能使 (mid) 变大(我们先不考虑与 (mid) 相同的数)。
那么问题就转化成二分一个 (mid),求出所有左端点在 ([a,b]) 之间,右端点在 ([c,d]) 之间的区间的大于 (mid) 的值的个数 (-) 小于 (mid) 的值的个数(令这个值为 (val))的最大值。如果这个最大值 (ge 0),(mid) 可能是中位数,增大 (mid),反之减小 (mid)。
([b+1,c-1]) 里面的数肯定要取,那么接着考虑的就是找出 (x,y),使 ([x,b],[c,y]) 区间的 (val) 的和最大。
其实那两个区间我们分别知道了右端点和左端点,其实就是求后缀/前缀最大。
如何求 (val)?有个很经典的套路:在原序列将比 (mid) 小的数的位置赋值为 (-1),大于 (mid) 和 (mid) 本身的位置赋值为 (1)。这样查询 ([l,r]) 的权值和就是 (val) 值。
那么 (val) 显然用线段树维护。不过还有一个问题:对于每种权值,显然之前的 (-1/1) 赋值是不一样的。
难道对于每个权值建一棵线段树(即 (n) 棵线段树)?
显然时空都无法承受(动态开点也不行,因为是查询区间)。
我们注意到对于相邻的两种权值 (x<y),(y) 相对于 (x) 只是 (x) 对应的下标的赋值从 (1) 变成 (-1)。
所以用可持久化线段树就行了,空间复杂度 (nlog n),时间复杂度 (mathcal O(Qlog^2n))。
( ext{Q&A})
( ext Q1):不会出现二分的数不在区间内吗?
( ext A1):中位数看的是下标,显然区间会有大于二分值的数满足这个条件,我们继续往上调二分值,一定会在区间内。
( ext Q2):如何处理权值相等的情况?
( ext A2):将权值排序后,可以直接强制权值相等的大小关系。为什么是对的?首先这不影响权值与不同权值的大小关系,其次二分是一定可以分到正确的那个位置的。
( ext{Code})
#include <cstdio>
#define rep(i,_l,_r) for(register signed i=(_l),_end=(_r);i<=_end;++i)
#define fep(i,_l,_r) for(register signed i=(_l),_end=(_r);i>=_end;--i)
#define erep(i,u) for(signed i=head[u],v=to[i];i;i=nxt[i],v=to[i])
#define efep(i,u) for(signed i=Head[u],v=to[i];i;i=nxt[i],v=to[i])
#define print(x,y) write(x),putchar(y)
template <class T> inline T read(const T sample) {
T x=0; int f=1; char s;
while((s=getchar())>'9'||s<'0') if(s=='-') f=-1;
while(s>='0'&&s<='9') x=(x<<1)+(x<<3)+(s^48),s=getchar();
return x*f;
}
template <class T> inline void write(const T x) {
if(x<0) return (void) (putchar('-'),write(-x));
if(x>9) write(x/10);
putchar(x%10^48);
}
template <class T> inline T Max(const T x,const T y) {if(x>y) return x; return y;}
template <class T> inline T Min(const T x,const T y) {if(x<y) return x; return y;}
template <class T> inline T fab(const T x) {return x>0?x:-x;}
template <class T> inline T gcd(const T x,const T y) {return y?gcd(y,x%y):x;}
template <class T> inline T lcm(const T x,const T y) {return x/gcd(x,y)*y;}
#include <iostream>
#include <algorithm>
using namespace std;
const int maxn=2e4+5;
int n,lastans,val[maxn],p[maxn],rt[maxn],q,num[5];
namespace PDT {
int son[maxn*20][2],siz;
struct node {
int lmax,rmax,sum;
node operator + (node t) {
return (node) {
Max(lmax,sum+t.lmax),
Max(rmax+t.sum,t.rmax),
sum+t.sum
};
}
} t[maxn*20];
void Build(int &o,int l,int r) {
o=++siz;
if(l>=r) return (void)(t[o]=(node){1,1,1});
int mid=l+r>>1;
Build(son[o][0],l,mid); Build(son[o][1],mid+1,r);
t[o]=t[son[o][0]]+t[son[o][1]];
}
void Insert(int &o,int pre,int l,int r,int pos) {
o=++siz;
if(l>=r) return (void)(t[o]=(node){-1,-1,-1});
int mid=l+r>>1;
if(pos<=mid) son[o][1]=son[pre][1],Insert(son[o][0],son[pre][0],l,mid,pos);
else son[o][0]=son[pre][0],Insert(son[o][1],son[pre][1],mid+1,r,pos);
t[o]=t[son[o][0]]+t[son[o][1]];
}
node Query(int o,int l,int r,int L,int R) {
if(L>R) return (node){0,0,0};
if(l>=L&&r<=R) return t[o];
int mid=l+r>>1;
if(R<=mid) return Query(son[o][0],l,mid,L,R);
if(L>mid) return Query(son[o][1],mid+1,r,L,R);
return Query(son[o][0],l,mid,L,R)+Query(son[o][1],mid+1,r,L,R);
}
}
bool cmp(int x,int y) {
return val[x]<val[y];
}
bool ok(int x) {
int ans1=PDT::Query(rt[x],1,n,num[1]+1,num[2]-1).sum;
int ans2=PDT::Query(rt[x],1,n,num[0],num[1]).rmax;
int ans3=PDT::Query(rt[x],1,n,num[2],num[3]).lmax;
return ((ans1+ans2+ans3)>=0);
}
int main() {
n=read(9);
rep(i,1,n) val[i]=read(9),p[i]=i;
sort(p+1,p+n+1,cmp);
PDT::Build(rt[1],1,n);
rep(i,2,n) PDT::Insert(rt[i],rt[i-1],1,n,p[i-1]);
q=read(9);
while(q--) {
rep(i,0,3) num[i]=(lastans+read(9))%n;
sort(num,num+4);
rep(i,0,3) ++num[i];
int l=1,r=n,mid;
while(l<=r) {
mid=l+r>>1;
if(ok(mid)) lastans=mid,l=mid+1;
else r=mid-1;
}
print(val[p[lastans]],'
');
lastans=val[p[lastans]];
}
return 0;
}