[国家集训队]middle
题解
如果离线的话是一道很水的题,但它要求强制在线,但还是很水。
按它的定义来说,中位数是满足在本区间中,大于等于其的数比小于其的数更多的数中最大的一个数。它要求的又是要在所有可以选择的区间中使得其中位数最大的一个,我们就很容易地想到了二分。
我们可以先二分中位数是什么,由于中位数本身是不具有单调性的,我们必须要想个法子使其具有单调性,我们发现,如果就看大于它的数比小于它数的差距,明显是具有单调性的,于是我们就可以去二分了。
由于我们要它的区间不是固定的,我们需找到使其差值最大的一个区间。
我们假设我们最优的区间为
[
l
,
r
]
[l,r]
[l,r],一定满足
l
∈
[
l
1
,
l
2
]
,
r
∈
[
r
1
,
r
2
]
l\in [l1,l2],r\in[r1,r2]
l∈[l1,l2],r∈[r1,r2]。区间
[
l
,
r
]
[l,r]
[l,r]可以被拆成
[
l
,
l
2
]
,
(
l
2
,
r
1
)
,
[
r
1
,
r
]
[l,l2],(l2,r1),[r1,r]
[l,l2],(l2,r1),[r1,r]三个独立的区间。其中
(
l
2
,
r
1
)
(l2,r1)
(l2,r1)是固定的,可以先算出来,接下来我们只需要最出
l
,
r
l,r
l,r使得
[
l
,
l
2
]
[l,l2]
[l,l2]与
[
r
1
,
r
]
[r1,r]
[r1,r]的值最大即可,这个操作可以通过线段树来实现。
我们只需要对每个值建出一棵 0 / 1 0/1 0/1的线段树,在上面找出 [ l , l 2 ] [l,l2] [l,l2]与 [ r 1 , r ] [r1,r] [r1,r]的最大前缀与后缀。但这样做明显会使得空间炸掉,考虑可持久化。我们发现如果我们依值的顺序从小到大建树,值只会在一个位置从1变成-1,所以可持久化后的空间复杂度为 O ( n l o g n ) O\left(nlog\,n\right) O(nlogn),可以承受。
总时间复杂度 O ( q l o g 2 n ) O\left(qlog^2 \,n\right) O(qlog2n),可以过。
update in 2021.9.19:
事实上跟本不需要同时维护前缀后缀,只维护前缀,将
[
r
1
,
r
2
]
[r1,r2]
[r1,r2]中最大前缀与
[
l
1
−
1
,
l
2
−
1
]
[l1-1,l2-1]
[l1−1,l2−1]中最小前缀差分即可。
顺便换上一份跑得更快的代码。
源码
#include<bits/stdc++.h>
using namespace std;
#define MAXN 20005
#define lowbit(x) (x&-x)
#define reg register
#define pb push_back
#define mkpr make_pair
#define fir first
#define sec second
typedef long long LL;
typedef unsigned long long uLL;
const int INF=0x3f3f3f3f;
const int mo=1e9+7;
const int inv2=499122177;
const int jzm=2333;
const int lim=1e9;
const int n1=400;
const int zero=10000;
const int orG=3,invG=332748118;
const double Pi=acos(-1.0);
const double eps=1e-5;
typedef pair<int,int> pii;
template<typename _T>
_T Fabs(_T x){return x<0?-x:x;}
template<typename _T>
void read(_T &x){
_T f=1;x=0;char s=getchar();
while(s>'9'||s<'0'){if(s=='-')f=-1;s=getchar();}
while('0'<=s&&s<='9'){x=(x<<3)+(x<<1)+(s^48);s=getchar();}
x*=f;
}
template<typename _T>
void print(_T x){if(x<0){x=(~x)+1;putchar('-');}if(x>9)print(x/10);putchar(x%10+'0');}
LL gcd(LL a,LL b){return !b?a:gcd(b,a%b);}
int add(int x,int y,int p){return x+y<p?x+y:x+y-p;}
void Add(int &x,int y,int p){x=add(x,y,p);}
int qkpow(int a,int s,int p){int t=1;while(s){if(s&1LL)t=1ll*a*t%p;a=1ll*a*a%p;s>>=1LL;}return t;}
int n,q,a[MAXN],root[MAXN],ord[MAXN],li[5];
struct ming{int lson,rson,maxx,minn,sum;ming(){sum=maxx=minn=lson=rson=0;}};
class SegmentTree{
private:
ming tr[MAXN*30];int tot;
void pushup(int rt){
tr[rt].sum=tr[tr[rt].lson].sum+tr[tr[rt].rson].sum;
tr[rt].minn=min(tr[tr[rt].lson].minn,tr[tr[rt].lson].sum+tr[tr[rt].rson].minn);
tr[rt].maxx=max(tr[tr[rt].lson].maxx,tr[tr[rt].lson].sum+tr[tr[rt].rson].maxx);
}
public:
void insert(int &now,int las,int l,int r,int ai){
if(l>r||l>ai||r<ai)return ;int mid=l+r>>1;tr[now=++tot]=tr[las];
if(l==r){tr[now].maxx=tr[now].minn=tr[now].sum=-1;return ;}
if(ai<=mid)insert(tr[now].lson,tr[las].lson,l,mid,ai);
if(ai>mid)insert(tr[now].rson,tr[las].rson,mid+1,r,ai);
pushup(now);
}
int queryMin(int rt,int l,int r,int al,int ar){
if(l>r||l>ar||r<al||al>ar)return INF;
if(al<=l&&r<=ar)return tr[rt].minn;int mid=l+r>>1,res=INF;
if(al<=mid)res=min(res,queryMin(tr[rt].lson,l,mid,al,ar));
if(ar>mid)res=min(res,tr[tr[rt].lson].sum+queryMin(tr[rt].rson,mid+1,r,al,ar));
return res;
}
int queryMax(int rt,int l,int r,int al,int ar){
if(l>r||l>ar||r<al||al>ar)return -INF;
if(al<=l&&r<=ar)return tr[rt].maxx;int mid=l+r>>1,res=-INF;
if(al<=mid)res=max(res,queryMax(tr[rt].lson,l,mid,al,ar));
if(ar>mid)res=max(res,tr[tr[rt].lson].sum+queryMax(tr[rt].rson,mid+1,r,al,ar));
return res;
}
void build(int &rt,int l,int r){
rt=++tot;if(l==r){tr[rt].minn=tr[rt].maxx=tr[rt].sum=1;return ;}
int mid=l+r>>1;build(tr[rt].lson,l,mid);build(tr[rt].rson,mid+1,r);pushup(rt);
}
}T;
bool cmp(int x,int y){return a[x]<a[y];}
signed main(){
read(n);for(int i=1;i<=n;i++)read(a[i]),ord[i]=i;read(q);
sort(ord+1,ord+n+1,cmp);T.build(root[0],1,n);int lastans=0;
for(int i=1;i<=n;i++)T.insert(root[i],root[i-1],1,n,ord[i]);
for(int i=1;i<=q;i++){
for(int j=1;j<=4;j++)read(li[j]),li[j]=(li[j]%n+lastans)%n+1;
sort(li+1,li+5);int l=1,r=n;
while(l<r){
int mid=l+r+1>>1;
int tmpl=T.queryMin(root[mid-1],1,n,max(1,li[1]-1),li[2]-1);
int tmpr=T.queryMax(root[mid-1],1,n,li[3],li[4]);if(li[1]==1)tmpl=min(tmpl,0);
if(tmpr-tmpl>=0)l=mid;else r=mid-1;
}
printf("%d\n",lastans=a[ord[l]]);
}
return 0;
}