复习一下常见的trick。
求中位数转化为二分答案,大于等于的部分设置成 1 1 1 小的部分设置成 − 1 -1 −1然后求和,看结果是否大于等于 0 0 0 来判断是否可行。
这道题直接按照权值排序,以原序列标号为下标建立主席树,叶节点权值为在当前树中它应该为的权值,对于询问,中间的询问和,两边的询问最大前后缀即可。
代码:
#include
#define ll long long
#define re register
#define cs const
using std::cerr;
using std::cout;
cs int N=2e4+7;
namespace PST{
cs int N=::N*40;
struct atom{int lm,rm,sm;}t[N];
atom operator+(cs atom &a,cs atom &b){
return (atom){
std::max(a.lm,a.sm+b.lm),
std::max(b.rm,b.sm+a.rm),
a.sm+b.sm};
}
int lc[N],rc[N],tot;
void _copy(int &u){
int v=++tot;t[v]=t[u],lc[v]=lc[u],rc[v]=rc[u],u=v;
}
void build(int &u,int l,int r){
u=++tot;if(l==r){
t[u]={1,1,1};
return ;
}int m=(l+r)>>1;
build(lc[u],l,m);build(rc[u],m+1,r);
t[u]=t[lc[u]]+t[rc[u]];
}
void ins(int &u,int l,int r,int p){
_copy(u);if(l==r){
t[u]={-1,-1,-1};
return ;
}int m=(l+r)>>1;
p<=m?ins(lc[u],l,m,p):ins(rc[u],m+1,r,p);
t[u]=t[lc[u]]+t[rc[u]];
}
atom query(int u,int l,int r,int ql,int qr){
if((ql<=l&&r<=qr)||!u)return t[u];int m=(l+r)>>1;
if(qr<=m)return query(lc[u],l,m,ql,qr);
if(m<ql)return query(rc[u],m+1,r,ql,qr);
return query(lc[u],l,m,ql,qr)+query(rc[u],m+1,r,ql,qr);
}
}
int rt[N];
int n,a[N],b[N],bn;
std::vector<int> ps[N];
void Main(){
scanf("%d",&n);
for(int re i=0;i<n;++i)
scanf("%d",a+i),b[i]=a[i];
std::sort(b,b+n);
bn=std::unique(b,b+n)-b;
for(int i=0;i<n;++i)
ps[std::lower_bound(b,b+bn,a[i])-b].push_back(i);
PST::build(rt[0],0,n-1);
for(int re i=1;i<bn;++i){
rt[i]=rt[i-1];
for(int re v:ps[i-1])
PST::ins(rt[i],0,n-1,v);
}int Q,ans=0;scanf("%d",&Q);
while(Q--){
static int t[4];
for(int i=0;i<4;++i)
scanf("%d",t+i),t[i]=(t[i]+ans)%n;
std::sort(t,t+4);
int a=t[0],b=t[1],c=t[2],d=t[3];
int l=0,r=bn-1;
while(l<=r){
int m=(l+r)>>1;
int val=
PST::query(rt[m],0,n-1,a,b).rm+
PST::query(rt[m],0,n-1,c,d).lm+
((b+1<c)?PST::query(rt[m],0,n-1,b+1,c-1).sm:0);
if(val>=0)ans=::b[m],l=m+1;
else r=m-1;
}cout<<ans<<"\n";
}
}
inline void file(){
#ifdef zxyoi
freopen("middle.in","r",stdin);
#endif
}signed main(){file();Main();return 0;}