蒟蒻的垂死挣扎
这题写得。。。。不想说什么了。
首先,对于每次询问,我们可以知道,范围内比中位数小的数是要等于比他大的数的,而这题有重复就意味着小的数与大的数之差要在这个中位数个数的范围内,为了取到最大,我们自然是要它取到极致。我们考虑用二分答案来取到这个值,每次如果大于等于它的数 要大于等于 比他小的数,则这个答案是满足的,就可以继续缩范围,这样我们就得到了一个不错的思路。
那么我们如何快速解决check呢?我们注意到,虽然它所给的左右端点都不确定,在一个区间内,但是我们可以贪心地想一想,我们要二分出最优解,当然就要放过更多的二分值,所以就相当于我们要求[a,b]区间后缀的 比二分值大的数个数-比二分值小的数个数 的最大值,[c,d]区间前缀的同样值,再加上[b+1,c-1]区间的所有数,最后再判定该值是否可行。
而最最重要的就是这些值怎么求了。这里终于要用到最最最最最重要的主席树啦,我们对于每一个值开一棵线段数,比他小的数赋为-1,比他大的赋为1,就转化为了求最大前缀、后缀子段和的问题,轻松愉快。而对于赋值的问题,从小到大加数,初始全为1,每次转移就将上一个值赋为-1,这样就能完美操作。
这里注意插点顺序(wa了好多遍)
// luogu-judger-enable-o2
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<map>
#include<queue>
#include<set>
#define RG register
#define N 100100
#define ls tr[x].le
#define rs tr[x].ri
#define ll long long
#define ld long double
using namespace std;
inline ll read(){
RG ll x=0,o=1; RG char ch=getchar();
while((ch<'0'||ch>'9')&&ch!='-') ch=getchar();
if(ch=='-') o=-1,ch=getchar();
while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
return x*o;
}
int len,n,m,a[N],b[N],yyb[N],tot,top,root[N],id[N];
struct mona { int le,ri; int fr,la,sum; } tr[N<<6];
//位置从0开始标号!!!
inline void Pushup(RG int x){
tr[x].fr=max(tr[ls].fr,tr[ls].sum+tr[rs].fr);
tr[x].la=max(tr[rs].la,tr[rs].sum+tr[ls].la);
tr[x].sum=tr[ls].sum+tr[rs].sum;
}
inline void Build(RG int l,RG int r,RG int &x){
RG int num=r-l+1; x=++tot,tr[x]=(mona) { 0,0,num,num,num };
if(l==r) return ;
RG int mid=(l+r)>>1;
Build(l,mid,ls),Build(mid+1,r,rs);
}
inline void Modify(RG int l,RG int r,RG int &x,RG int w){
tr[++tot]=tr[x],x=tot;
if(l==r) { tr[x].fr=tr[x].la=tr[x].sum=-1; return ; }
RG int mid=(l+r)>>1;
if(w<=mid) Modify(l,mid,ls,w);
else Modify(mid+1,r,rs,w);
Pushup(x);
}
inline int Query_sum(RG int l,RG int r,RG int x,RG int L,RG int R){
if(R<L) return 0;
if(l==L&&r==R) return tr[x].sum;
RG int mid=(l+r)>>1;
if(R<=mid) return Query_sum(l,mid,ls,L,R);
else if(L>mid) return Query_sum(mid+1,r,rs,L,R);
else return (Query_sum(l,mid,ls,L,mid)+Query_sum(mid+1,r,rs,mid+1,R));
}
inline int Query_Fr(RG int l,RG int r,RG int x,RG int L,RG int R){
if(l==L&&r==R) return tr[x].fr;
RG int mid=(l+r)>>1;
if(R<=mid) return Query_Fr(l,mid,ls,L,R);
else if(L>mid) return Query_Fr(mid+1,r,rs,L,R);
else{
RG int Le=Query_Fr(l,mid,ls,L,mid),Ri=Query_Fr(mid+1,r,rs,mid+1,R);
RG int ss=Query_sum(l,mid,ls,L,mid);
return max(Le,ss+Ri);
}
}
inline int Query_La(RG int l,RG int r,RG int x,RG int L,RG int R){
if(l==L&&r==R) return tr[x].la;
RG int mid=(l+r)>>1;
if(R<=mid) return Query_La(l,mid,ls,L,R);
else if(L>mid) return Query_La(mid+1,r,rs,L,R);
else{
RG int Le=Query_La(l,mid,ls,L,mid),Ri=Query_La(mid+1,r,rs,mid+1,R);
RG int ss=Query_sum(mid+1,r,rs,mid+1,R);
return max(Ri,ss+Le);
}
}
bool cmp(RG int x,RG int y) { return a[x]<a[y]; }
int main(){
n=read(),Build(1,n,root[1]);
for(RG int i=1;i<=n;++i) a[i]=read(),id[i]=i;
m=read(),sort(id+1,id+1+n,cmp);
for(RG int i=2;i<=n;++i)
root[i]=root[i-1],Modify(1,n,root[i],id[i-1]);
for(RG int i=1,ans=0;i<=m;++i){
RG int Q[5];
for(RG int j=0;j<4;++j) Q[j]=(read()+ans)%n+1;
sort(Q,Q+4); RG int le=1,ri=n;
// for(RG int j=0;j<4;++j) cout<<Q[j]<<' ';cout<<endl;
while(le<=ri){
RG int mid=(le+ri)>>1;
RG int sum=Query_sum(1,n,root[mid],Q[1]+1,Q[2]-1);
RG int la=Query_La(1,n,root[mid],Q[0],Q[1]),fr=Query_Fr(1,n,root[mid],Q[2],Q[3]);
if(la+fr+sum>=0) ans=a[id[mid]],le=mid+1;
else ri=mid-1; //cout<<mid<<' '<<sum<<' '<<la<<' '<<fr<<endl;
} printf("%d\n",ans);
} return 0;
}