题意
一个长度为n的序列a,设其排过序之后为b,其中位数定义为b[n/2],其中a,b从0开始标号,除法取下整。给你一个长度为n的序列s。回答Q个这样的询问:s的左端点在[a,b]之间,右端点在[c,d]之间的子序列中,最大的中位数。其中a<b<c<d。位置也从0开始标号。我会使用一些方式强制你在线。
\(n \leq 20000,Q \leq 25000\)
分析
对多个区间取中位数的最大值,但是这些区间都包含一个相同的区间[b+1,c-1]。
可以将[b+1,c-1]区间先排序,再考虑往左右两边扩展。
对于左右两边扩展的数x,扩展后能否更优其实是由x与[b+1,c-1]中位数M的大小决定的。
考虑数列长度对中位数的影响。
- 奇数:中间的数
- 偶数:中间靠后的数
那么相当于x=M时也会可能对中位数的变大造成贡献。
因此将大于等于M的x看作1,小于M的x看作-1
如果能在两端找一段和>0,那么M就可能可以变大。
但是两端是分开的,如果要连起来需要考虑中间的部分。
如果中间的一部分也按类似考虑,那么就是检验M是否可行了。
然后须要用到中间[b+1,b-1]部分的和,[a,b]的最大后缀和,[c,d]的最大前缀和,用线段树可以很好解决。
可能出现的M最多只有N种,离散化。然后就可以对于每个M都预处理一遍这个正负1线段树。
但是不可能开N颗线段树。
发现M时候的情况从M-1转移,所需要改变的只有M-1的值。
于是用可持久化线段树就可以解决了。
时间复杂度\(O(N \log N+Q \log^2 N)\)
代码
#include<cstdlib>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<ctime>
#include<iostream>
#include<string>
#include<vector>
#include<list>
#include<deque>
#include<stack>
#include<queue>
#include<map>
#include<set>
#include<bitset>
#include<algorithm>
#include<complex>
#pragma GCC optimize ("O0")
using namespace std;
template<class T> inline T read(T&x)
{
T data=0;
int w=1;
char ch=getchar();
while(!isdigit(ch))
{
if(ch=='-')
w=-1;
ch=getchar();
}
while(isdigit(ch))
data=10*data+ch-'0',ch=getchar();
return x=data*w;
}
typedef long long ll;
const int INF=0x7fffffff;
const int MAXN=2e4+7,MAXT=MAXN*20; // 16+4
int n;
struct node
{
int v,p;
bool operator<(const node&rhs)const
{
return v<rhs.v;
}
}a[MAXN]; // basz 0 to simplify querying
int sz,root[MAXN];
struct PreSegTree
{
int L[MAXT],R[MAXT];
int sum[MAXT],pre[MAXT],suf[MAXT];
void pushup(int now)
{
sum[now]=sum[L[now]]+sum[R[now]];
pre[now]=max(pre[L[now]],sum[L[now]]+pre[R[now]]);
suf[now]=max(suf[R[now]],sum[R[now]]+suf[L[now]]);
}
void build(int&now,int l,int r)
{
now=++sz;
if(l==r)
{
sum[now]=pre[now]=suf[now]=1;
return;
}
int mid=(l+r)>>1;
build(L[now],l,mid);
build(R[now],mid+1,r);
pushup(now);
}
int qsum(int now,int l,int r,int ql,int qr)
{
if(ql<=l&&r<=qr)
{
return sum[now];
}
int mid=(l+r)>>1,ans=0;
if(ql<=mid)
ans+=qsum(L[now],l,mid,ql,qr);
if(qr>=mid+1)
ans+=qsum(R[now],mid+1,r,ql,qr);
return ans;
}
int qpre(int now,int l,int r,int ql,int qr)
{
if(ql<=l&&r<=qr)
{
return pre[now];
}
int mid=(l+r)>>1;
if(qr<=mid)
return qpre(L[now],l,mid,ql,qr);
if(ql>=mid+1)
return qpre(R[now],mid+1,r,ql,qr);
return max(qpre(L[now],l,mid,ql,qr),qsum(L[now],l,mid,ql,qr)+qpre(R[now],mid+1,r,ql,qr));
}
int qsuf(int now,int l,int r,int ql,int qr)
{
if(ql<=l&&r<=qr)
{
return suf[now];
}
int mid=(l+r)>>1;
if(qr<=mid)
return qsuf(L[now],l,mid,ql,qr);
if(ql>=mid+1)
return qsuf(R[now],mid+1,r,ql,qr);
return max(qsuf(R[now],mid+1,r,ql,qr),qsum(R[now],mid+1,r,ql,qr)+qsuf(L[now],l,mid,ql,qr));
}
void change(int&now,int l,int r,int p,int v)
{
++sz;
L[sz]=L[now],R[sz]=R[now];
now=sz;
if(l==r)
{
sum[now]=pre[now]=suf[now]=v;
return;
}
int mid=(l+r)>>1;
if(p<=mid)
change(L[now],l,mid,p,v);
else
change(R[now],mid+1,r,p,v);
pushup(now);
}
}T;
int q[4];
bool judge(int rt,int a,int b,int c,int d)
{
int res=0;
if(b+1<=c-1)
res+=T.qsum(rt,0,n-1,b+1,c-1);
res+=T.qsuf(rt,0,n-1,a,b);
res+=T.qpre(rt,0,n-1,c,d);
return res>=0;
}
int main()
{
// freopen(".in","r",stdin);
// freopen(".out","w",stdout);
read(n);
for(int i=0;i<n;++i)
{
read(a[i].v);
a[i].p=i;
}
sort(a,a+n); // 特殊离散化,后面二分答案的排名
T.build(root[0],0,n-1);
for(int i=1;i<n;++i)
{
root[i]=root[i-1];
T.change(root[i],0,n-1,a[i-1].p,-1); // change the one 1 smaller than it
}
int Q,ans=0;
read(Q);
while(Q--)
{
read(q[0]);read(q[1]);
read(q[2]);read(q[3]);
(q[0]+=ans)%=n,(q[1]+=ans)%=n,
(q[2]+=ans)%=n,(q[3]+=ans)%=n;
sort(q,q+4);
int l=0,r=n-1,x;
while(l<=r)
{
int mid=(l+r)>>1;
if(judge(root[mid],q[0],q[1],q[2],q[3]))
x=mid,l=mid+1;
else
r=mid-1;
}
ans=a[x].v;
printf("%d\n",ans);
}
// fclose(stdin);
// fclose(stdout);
return 0;
}