[BZOJ2653]middle
sol
cls的题目,写起来挺容易但思维的确是挺神的。
求中位数可以先二分中位数,然后把所有大于等于这个数的全部设为1,把所有小于这个数队全部设为-1,然后查一下区间总和是否大于等于0即可判断是否合法。
但是显然不能对于每次询问这么搞一下,所以事先建立n棵主席树即可。
查询的时候就是查[a,b]的最大后缀子段和,[b+1,c-1]的和,以及[c,d]的最大前缀子段和。判加起来是否大于等于0。
code
#include<cstdio>
#include<algorithm>
using namespace std;
int gi()
{
int x=0,w=1;char ch=getchar();
while ((ch<'0'||ch>'9')&&ch!='-') ch=getchar();
if (ch=='-') w=0,ch=getchar();
while (ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
return w?x:-x;
}
const int N = 2e4+5;
struct Data{
int vl,vr,sum;
Data operator + (const Data b) const
{
return (Data){max(vl,sum+b.vl),max(b.vr,b.sum+vr),sum+b.sum};
}
};
struct president_tree{int ls,rs;Data v;}t[N*50];
int n,m,a[N],id[N],rt[N],tot,q[4],ans;
bool cmp(int i,int j){return a[i]<a[j];}
void build(int &x,int l,int r)
{
x=++tot;
if (l==r) {t[x].v=(Data){1,1,1};return;}
int mid=l+r>>1;
build(t[x].ls,l,mid);build(t[x].rs,mid+1,r);
t[x].v=t[t[x].ls].v+t[t[x].rs].v;
}
void modify(int &x,int l,int r,int p)
{
t[++tot]=t[x];x=tot;
if (l==r) {t[x].v=(Data){-1,-1,-1};return;}
int mid=l+r>>1;
if (p<=mid) modify(t[x].ls,l,mid,p);
else modify(t[x].rs,mid+1,r,p);
t[x].v=t[t[x].ls].v+t[t[x].rs].v;
}
Data query(int x,int l,int r,int ql,int qr)
{
if (l>=ql&&r<=qr) return t[x].v;
int mid=l+r>>1;
if (qr<=mid) return query(t[x].ls,l,mid,ql,qr);
if (ql>mid) return query(t[x].rs,mid+1,r,ql,qr);
return query(t[x].ls,l,mid,ql,qr)+query(t[x].rs,mid+1,r,ql,qr);
}
int main()
{
n=gi();
for (int i=1;i<=n;++i) a[i]=gi(),id[i]=i;
sort(id+1,id+n+1,cmp);
build(rt[1],1,n);
for (int i=2;i<=n;++i) modify(rt[i]=rt[i-1],1,n,id[i-1]);
m=gi();
while (m--)
{
for (int i=0;i<4;++i) q[i]=(gi()+ans)%n+1;
sort(q,q+4);
int l=1,r=n;
while (l<r)
{
int mid=l+r+1>>1,tmp=0;
tmp+=query(rt[mid],1,n,q[0],q[1]).vr;
tmp+=query(rt[mid],1,n,q[2],q[3]).vl;
if (q[1]+1<=q[2]-1) tmp+=query(rt[mid],1,n,q[1]+1,q[2]-1).sum;
if (tmp>=0) l=mid;
else r=mid-1;
}
printf("%d\n",ans=a[id[l]]);
}
return 0;
}