二分+主席树
个人感觉这道题非常好,思维难度高,代码好写(然而自己写的时候神志不清,转态全无qwq)
首先要知道一个关于中位数的基本套路,将大于等于它的赋值成1,小于它的赋值成-1
然后通过区间和就能找出中位数了
对于本题来说:
首先显然答案满足单调性,因为二分的是第k大的数,所以1,-1这个序列和肯定是单调变化的
其次我们考虐将[a,b],[c,d]区间转化成[a,b],(b,c),[c,d]
所以我们只需查询[a,b]中的最大右连续子段和(必选一个),(b,c)的区间和,[c,d]的最大左连续子段和(必选一个)
若这三个和>=0,则说明当前二分的第k大的数是ok的,否则不行,然后继续二分
对于维护每个数作为中位数时的1,-1序列,我们用主席树维护一下就ok了
效率:O(n(logn)^3)
/**************************************************************
Problem: 2653
User: syh0313
Language: C++
Result: Accepted
Time:2156 ms
Memory:12788 kb
****************************************************************/
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <string>
#include <cmath>
#include <algorithm>
#include <map>
#define lch a[n].lc
#define rch a[n].rc
using
namespace
std;
const
int
maxn=20010;
int
n,m,v[maxn],vv[maxn],root[maxn],linum,line[maxn],topt,lim,t;
int
cnt,to[maxn],st[maxn],nt[maxn],q[5],ans;
struct
da{
int
lc,rc,sum,suml,sumr,l,r;}a[maxn*20];
map<
int
,
int
>lc;
void
add(
int
x,
int
y)
{to[++cnt]=y; nt[cnt]=st[x]; st[x]=cnt;}
void
updata(
int
n)
{
a[n].sum=a[lch].sum+a[rch].sum;
a[n].suml=max(a[lch].suml,a[lch].sum+a[rch].suml);
a[n].sumr=max(a[rch].sumr,a[rch].sum+a[lch].sumr);
}
void
build_tree(
int
&n,
int
l,
int
r)
{
n=++topt; a[n].l=l; a[n].r=r;
if
(l==r)
{
a[n].sum=a[n].suml=a[n].sumr=1;
return
;
}
int
mid=(l+r)>>1;
build_tree(lch,l,mid); build_tree(rch,mid+1,r);
updata(n);
}
void
tree_add(
int
old,
int
&n,
int
l,
int
r,
int
lc)
{
if
(n<=lim) n=++topt;
if
(l==r) {a[n].sum=a[n].suml=a[n].sumr=-1;
return
;}
int
mid=(l+r)>>1;
if
(lc<=mid)
{
if
(rch<=lim) rch=a[old].rc;
tree_add(a[old].lc,lch,l,mid,lc);
}
else
{
if
(lch<=lim) lch=a[old].lc;
tree_add(a[old].rc,rch,mid+1,r,lc);
}
updata(n);
}
int
qsum(
int
n,
int
L,
int
R,
int
l,
int
r)
{
if
(l>r)
return
0;
if
(L==l && R==r)
return
a[n].sum;
int
mid=(L+R)>>1;
if
(r<=mid)
return
qsum(lch,L,mid,l,r);
else
if
(l>=mid+1)
return
qsum(rch,mid+1,R,l,r);
else
return
qsum(lch,L,mid,l,mid)+qsum(rch,mid+1,R,mid+1,r);
}
int
qsuml(
int
n,
int
L,
int
R,
int
l,
int
r,
int
k)
{
if
(L==l && R==r)
return
a[n].suml;
int
mid=(L+R)>>1;
if
(r<=mid)
return
qsuml(lch,L,mid,l,r,k);
else
if
(l>=mid+1)
return
qsuml(rch,mid+1,R,l,r,k);
else
{
int
kk=qsum(root[k],0,t,l,mid)+qsuml(rch,mid+1,R,mid+1,r,k);
return
max(kk,qsuml(lch,L,mid,l,mid,k));
}
}
int
qsumr(
int
n,
int
L,
int
R,
int
l,
int
r,
int
k)
{
if
(L==l && R==r)
return
a[n].sumr;
int
mid=(L+R)>>1;
if
(r<=mid)
return
qsumr(lch,L,mid,l,r,k);
else
if
(l>=mid+1)
return
qsumr(rch,mid+1,R,l,r,k);
else
{
int
kk=qsum(root[k],0,t,mid+1,r)+qsumr(lch,L,mid,l,mid,k);
return
max(kk,qsumr(rch,mid+1,R,mid+1,r,k));
}
}
int
main()
{
scanf
(
"%d"
,&n); t=n-1;
for
(
int
i=0;i<n;i++)
scanf
(
"%d"
,&v[i]),vv[i]=v[i];
sort(vv,vv+n);
for
(
int
i=0;i<n;i++)
if
(!lc[vv[i]]) lc[vv[i]]=++linum,line[linum]=vv[i];
for
(
int
i=0;i<n;i++) add(lc[v[i]],i);
build_tree(root[1],0,n-1);
for
(
int
i=2;i<=linum;i++)
{
int
p=st[i-1]; lim=topt;
while
(p) {tree_add(root[i-1],root[i],0,n-1,to[p]); p=nt[p];}
}
scanf
(
"%d"
,&m);
while
(m--)
{
for
(
int
i=1;i<=4;i++)
scanf
(
"%d"
,&q[i]),q[i]=(q[i]+ans)%n;
sort(q+1,q+5);
int
l=1,r=linum; ans=0;
while
(l<=r)
{
int
mid=(l+r)>>1;
int
ss=qsum(root[mid],0,n-1,q[2]+1,q[3]-1);
ss+=qsumr(root[mid],0,n-1,q[1],q[2],mid);
ss+=qsuml(root[mid],0,n-1,q[3],q[4],mid);
if
(ss>=0) {ans=max(ans,mid); l=mid+1;}
else
r=mid-1;
}
ans=line[ans];
printf
(
"%d\n"
,ans);
}
return
0;
}