description
给定一个序列 s s s, q q q次询问,每次询问所有左端点在 [ a , b ] [a,b] [a,b] ,右端点在 [ c , d ] [c,d] [c,d] 里面的区间的中位数的最大值,强制在线
s u b t a s k 1 : n , q ≤ 100 subtask\ 1:n,q\leq 100 subtask 1:n,q≤100
s u b t a s k 2 : n ≤ 2000 , m ≤ 2.5 × 1 0 4 subtask\ 2:n\leq 2000,m\leq 2.5\times 10^4 subtask 2:n≤2000,m≤2.5×104
s u b t a s k 3 : n , q ≤ 2.5 × 1 0 4 subtask\ 3:n,q\leq 2.5\times 10^4 subtask 3:n,q≤2.5×104
solution
s u b t a s k 1 : subtask\ 1: subtask 1:
瞎暴力即可
s u b t a s k 2 : subtask\ 2: subtask 2:
我们可以先预处理出任意的 [ l , r ] [l,r] [l,r]的中位数,用一个二维线段树之类的东西储存,每次查询相当于是平面上一个矩形求最大值
s u b t a s k 3 : subtask\ 3: subtask 3:
一个数能不能是区间的中位数的判定方法可以把所有 ≥ \geq ≥这个数的都变成 1 1 1,其他的变成 − 1 -1 −1,每次查询区间和就可以判定
我们考虑二分答案,对于每一个权值建立可持久化线段树,每次check的时候,相当于看存不存在一个左端点在 [ a , b ] [a,b] [a,b]内,右端点在 [ c , d ] [c,d] [c,d]的区间,使得这个区间的和 ≥ 0 \geq 0 ≥0
其实这个东西本质上是 s u m [ b , c ] + s u f m a x [ a , b ) + p r e m a x ( c , d ] sum[b,c]+sufmax[a,b)+premax(c,d] sum[b,c]+sufmax[a,b)+premax(c,d]
所以我们用可持久化线段树维护区间和,区间前缀最大值,区间后缀最大值就可以啦
时间复杂度 O ( q log 2 n ) O(q\log^2n) O(qlog2n)
空间复杂度 O ( n log n ) O(n\log n) O(nlogn)
#include
using namespace std;
# define Rep(i,a,b) for(int i=a;i<=b;i++)
# define _Rep(i,a,b) for(int i=a;i>=b;i--)
# define RepG(i,u) for(int i=head[u];~i;i=e[i].next)
typedef long long ll;
const int N=1e5+5;
template<typename T> void read(T &x){
x=0;int f=1;
char c=getchar();
for(;!isdigit(c);c=getchar())if(c=='-')f=-1;
for(;isdigit(c);c=getchar())x=(x<<1)+(x<<3)+c-'0';
x*=f;
}
int n,q;
int head[N],cnt;
int b[N],sz;
int root[N],tot;
int in[5],ans;
struct misaka{
int val,id;
bool operator < (const misaka &cmp)const{
return val<cmp.val;
}
}a[N];
struct mikoto{
int lc,rc;
int lmax,rmax,sum;
}seg[N*30];
mikoto merge(mikoto l,mikoto r){
mikoto res;
res.lmax=max(l.lmax,l.sum+r.lmax);
res.rmax=max(r.rmax,r.sum+l.rmax);
res.sum=l.sum+r.sum;
return res;
}
void pushup(int u){
seg[u].lmax=max(seg[seg[u].lc].lmax,seg[seg[u].lc].sum+seg[seg[u].rc].lmax);
seg[u].rmax=max(seg[seg[u].rc].rmax,seg[seg[u].rc].sum+seg[seg[u].lc].rmax);
seg[u].sum=seg[seg[u].lc].sum+seg[seg[u].rc].sum;
}
int build(int l,int r){
int u=++tot;
if(l==r){
seg[u].lmax=seg[u].rmax=0;
seg[u].sum=-1;
return u;
}
int mid=l+r>>1;
seg[u].lc=build(l,mid);
seg[u].rc=build(mid+1,r);
pushup(u);
return u;
}
int update(int o,int l,int r,int x,int k){
int u=++tot;
seg[u]=seg[o];
if(l==r){
seg[u].sum=seg[u].lmax=seg[u].rmax=k;
return u;
}
int mid=l+r>>1;
if(x<=mid)seg[u].lc=update(seg[u].lc,l,mid,x,k);
else seg[u].rc=update(seg[u].rc,mid+1,r,x,k);
pushup(u);
return u;
}
mikoto query(int u,int l,int r,int ql,int qr){
if(l>=ql&&r<=qr)return seg[u];
int mid=l+r>>1;
if(qr<=mid)return query(seg[u].lc,l,mid,ql,qr);
if(ql>mid)return query(seg[u].rc,mid+1,r,ql,qr);
return merge(query(seg[u].lc,l,mid,ql,qr),query(seg[u].rc,mid+1,r,ql,qr));
}
bool check(int mid){
int res=0;
if(in[1]<=in[2]-1)res+=query(root[mid],1,n,in[1],in[2]-1).rmax;
res+=query(root[mid],1,n,in[2],in[3]).sum;
if(in[3]+1<=in[4])res+=query(root[mid],1,n,in[3]+1,in[4]).lmax;
return res>=0;
}
int main()
{
read(n);
Rep(i,1,n)read(a[i].val),a[i].id=i,b[i]=a[i].val;
sort(b+1,b+n+1);
sz=unique(b+1,b+n+1)-b-1;
Rep(i,1,n)a[i].val=lower_bound(b+1,b+sz+1,a[i].val)-b;
sort(a+1,a+n+1);
root[sz+1]=build(1,n);
int now=n;
_Rep(i,sz,1){
root[i]=root[i+1];
while(now&&a[now].val==i)root[i]=update(root[i],1,n,a[now].id,1),now--;
}
read(q);
while(q--){
Rep(i,1,4)read(in[i]);
Rep(i,1,4)in[i]=(in[i]+ans)%n+1;
sort(in+1,in+4+1);
int l=1,r=sz,res=0;
while(l<=r){
int mid=l+r>>1;
if(check(mid))res=mid,l=mid+1;
else r=mid-1;
}
ans=b[res];
printf("%d\n",ans);
}
return 0;
}