题意:
给定一个序列,多次询问左端点在[a,b],右端点在[c,d]的区间的中位数的最大值。
解析:
乍一看题,这什么玩意。
冷静冷静。
寻求中位数最大值?
上二分可不可以?
有没有单调性?
。。。。
我们发现,居然tmd有单调性?
我也是醉了
我们可以二分出来当前某个中位数。
然后我们把序列里所有的比它小的数看成-1,所有的比它大的数看成1。
然后我们只需要找左端点在[a,b],右端点在[c,d]是否存在一个区间使得该区间的和大于等于0
如果有的话,那么显然答案是可以再递增的,反之就需要减小答案。
然后我们就可以采取这种方式来二分中位数了。
但是值得注意的是,我们不能每一次对序列中的所有元素重新赋值,那样的话根本遭不住。
这时候可持久化数据结构的优势就体现出来了。
我们可以用主席树来解决这个问题。
具体方案就是我们把序列中的元素排序,从小到大加入。
初始把每个元素的值看作是1
然后每一次加入就相当于把上一个的值变成-1.(相等也可以改上一个不会影响答案)
这样我们每一次加入都是在原来的历史版本上重新搞一个新版本,主席树即可搞定。
至于二分部分。
左端点在[a,b],右端点在[c,d]中的某一段最大和区间。
显然(b,c)是必须取的。
于是就是rmax([a,b])+sum((b,c))+lmax([c,d])。
线段树里维护一个从左起的最大连续和以及从右起的最大连续和以及整段和即可。
注意上方()与[]的区别。
代码:
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define N 20010
#define Q 25010
#define M 401000
#define INF 0x3f3f3f3f
using namespace std;
int n,NO,q;
int root[N];
int size;
struct node
{
int lson,rson,sum,lmax,rmax;
}seg[M];
struct element
{
int val,no;
friend istream& operator >> (istream &_,element &a)
{scanf("%d",&a.val),a.no=++NO;return _;}
}a[N];
int cmp(element a,element b)
{
return a.val<b.val;
}
void pushup(int rt)
{
int l=seg[rt].lson,r=seg[rt].rson;
seg[rt].sum=seg[l].sum+seg[r].sum;
seg[rt].lmax=max(seg[l].lmax,seg[l].sum+seg[r].lmax);
seg[rt].rmax=max(seg[r].rmax,seg[r].sum+seg[l].rmax);
}
void build(int &rt,int l,int r)
{
if(!rt)
rt=++size;
if(l==r)
{
seg[rt].lmax=seg[rt].rmax=seg[rt].sum=1;
return;
}
int mid=(l+r)>>1;
build(seg[rt].lson,l,mid);
build(seg[rt].rson,mid+1,r);
pushup(rt);
}
void update(int y,int &x,int l,int r,int v,int val)
{
x=++size;
seg[x]=seg[y];
if(l==r)
{
seg[x].lmax=seg[x].rmax=seg[x].sum=val;
return;
}
int mid=(l+r)>>1;
if(v<=mid)update(seg[y].lson,seg[x].lson,l,mid,v,val);
else update(seg[y].rson,seg[x].rson,mid+1,r,v,val);
pushup(x);
}
int query_sum(int rt,int L,int R,int l,int r)
{
if(L>R)return 0;
int ret=0;
if(L<=l&&r<=R)
{
return seg[rt].sum;
}
int mid=(l+r)>>1;
if(L<=mid)ret+=query_sum(seg[rt].lson,L,R,l,mid);
if(R>mid)ret+=query_sum(seg[rt].rson,L,R,mid+1,r);
return ret;
}
int query_rmax(int rt,int L,int R,int l,int r)
{
if(L>R)return 0;
int ret=-INF;
if(L<=l&&r<=R)
{
return seg[rt].rmax;
}
int mid=(l+r)>>1;
if(R<=mid)ret=max(ret,query_rmax(seg[rt].lson,L,R,l,mid));
else if(L>mid)ret=max(ret,query_rmax(seg[rt].rson,L,R,mid+1,r));
else
{
ret=max(ret,query_rmax(seg[rt].lson,L,mid,l,mid)+query_sum(seg[rt].rson,mid+1,R,mid+1,r));
ret=max(ret,query_rmax(seg[rt].rson,mid+1,R,mid+1,r));
}
return ret;
}
int query_lmax(int rt,int L,int R,int l,int r)
{
if(L>R)return 0;
int ret=-INF;
if(L<=l&&r<=R)
{
return seg[rt].lmax;
}
int mid=(l+r)>>1;
if(R<=mid)ret=max(ret,query_lmax(seg[rt].lson,L,R,l,mid));
else if(L>mid)ret=max(ret,query_lmax(seg[rt].rson,L,R,mid+1,r));
else
{
ret=max(ret,query_sum(seg[rt].lson,L,mid,l,mid)+query_lmax(seg[rt].rson,mid+1,R,mid+1,r));
ret=max(ret,query_lmax(seg[rt].lson,L,mid,l,mid));
}
return ret;
}
int query[5];
bool check(int x)
{
return query_rmax(root[x],query[1],query[2],1,n)+query_sum(root[x],query[2]+1,query[3]-1,1,n)+query_lmax(root[x],query[3],query[4],1,n)>=0;
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)cin>>a[i];
sort(a+1,a+n+1,cmp);
build(root[1],1,n);
for(int i=2;i<=n;i++)
update(root[i-1],root[i],1,n,a[i-1].no,-1);
int ans=0;
scanf("%d",&q);
for(int i=1;i<=q;i++)
{
scanf("%d%d%d%d",&query[1],&query[2],&query[3],&query[4]);
for(int i=1;i<=4;i++)query[i]=(query[i]+ans)%n+1;
sort(query+1,query+5);
int tmp=0,l=1,r=n;
while(l<=r)
{
int mid=(l+r)>>1;
if(check(mid))tmp=mid,l=mid+1;
else r=mid-1;
}
ans=a[tmp].val;
printf("%d\n",ans);
}
}