分析:
可以发现所有满足条件的中位数是具有单调性的
也就是说,如果
M'<M
M
′
<
M
,且
M
M
为合法的中位数,那么也为合法的中位数
那么我们就可以考虑二分答案了
根据题目中对中位数的定义:
如果序列长度为奇数,中位数即为最中间的那个数,
如果序列长度为偶数,那么中位数为中间的两个数更靠后的那个数
假设我们已经二分出答案mid,现在要判定M是否为合法的中位数
只需要将
≥M
≥
M
的数的权定为
1
1
,将的数的权定为
−1
−
1
如果一个区间的最大连续子序列和≥0
就说明
M
M
是合法的中位数,即一定存在一种方案,使这个区间的某一个子序列的中位数是
怎样维护区间最大连续子序列和呢?
立马想到类似线段树之类的数据结构
结点内只需要维护:
sum
s
u
m
:区间和
ls
l
s
:从左端开始的最大连续子序列和
rs
r
s
:从右端开始的最大连续子序列和
分别表示,从左端起最大连续子序列和
那么满足条件
[a,b]−[c,d]
[
a
,
b
]
−
[
c
,
d
]
的最长连续子序列和即为
sum(b,c)+ls(a,b)+rs(c,d)
s
u
m
(
b
,
c
)
+
l
s
(
a
,
b
)
+
r
s
(
c
,
d
)
可是如果每一次二分都重构线段树的话时间是无法承受的
但是想到从1到−1的变化实际上是通过M的移动产生的
考虑建立可持久化线段树
其中维护的是每个结点作为中位数时的线段树信息
首先第一棵线段树所有的结点的权都为1
第二棵线段树就是在第一棵的基础上,将排名为第一的点的位置的权修改为−1
这就相当于是当M为第二个点的时候的线段树了
询问的时候直接在M代表的线段树上区间查询即可
最坏情况下时间复杂度 O(nlog3n) O ( n l o g 3 n )
tip
很好的一道题,主席树的使用方法又拓宽了
清楚了思路后,马上就写完了
没有debug,直接过样例,交上去一A撒花
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define ll long long
using namespace std;
const int N=20005;
int n,m,root[N],q[4],top=0,lastans=0;
struct point{
int x,id;
bool operator <(const point &a) const
{
return x<a.x;
}
};
point A[N];
struct node{
int ls,rs,sum,l,r;
};
node t[N*20];
void build(int &now,int l,int r)
{
top++;
t[top]=t[now];
now=top;
t[now].sum=t[now].ls=t[now].rs=r-l+1;
if (l==r) return;
int mid=(l+r)>>1;
build(t[now].l,l,mid);
build(t[now].r,mid+1,r);
}
node update(int lc,int rc)
{
node ans;
ans.l=lc; ans.r=rc;
ans.sum=t[lc].sum+t[rc].sum;
ans.ls=max(t[lc].ls,t[lc].sum+t[rc].ls);
ans.rs=max(t[rc].rs,t[rc].sum+t[lc].rs);
return ans;
}
node update_2(node lc,node rc)
{
node ans;
ans.sum=lc.sum+rc.sum;
ans.ls=max(lc.ls,lc.sum+rc.ls);
ans.rs=max(rc.rs,rc.sum+lc.rs);
return ans;
}
void insert(int &now,int l,int r,int x) //位置x -1
{
top++;
t[top]=t[now];
now=top;
if (l==r)
{
t[now].sum=-1; t[now].ls=t[now].rs=0;
return;
}
int mid=(l+r)>>1;
if (x<=mid) insert(t[now].l,l,mid,x);
else insert(t[now].r,mid+1,r,x);
t[now]=update(t[now].l,t[now].r);
}
node ask(int x,int l,int r,int L,int R)
{
if (l>=L&&r<=R)
return t[x];
int mid=(l+r)>>1;
if (R<=mid) return ask(t[x].l,l,mid,L,R);
else if (L>mid) return ask(t[x].r,mid+1,r,L,R);
else return update_2(ask(t[x].l,l,mid,L,R),ask(t[x].r,mid+1,r,L,R));
}
void solve(int a,int b,int c,int d)
{
int ans=0;
int l=1,r=n;
while (l<=r)
{
int mid=(l+r)>>1;
int m1=ask(root[mid],1,n,b,c).sum;
int m2=ask(root[mid],1,n,a,b-1).rs;
int m3=ask(root[mid],1,n,c+1,d).ls;
if (m1+m2+m3>=0) ans=mid,l=mid+1;
else r=mid-1;
}
printf("%d\n",A[ans].x);
lastans=A[ans].x;
}
int main()
{
scanf("%d",&n);
for (int i=1;i<=n;i++) scanf("%d",&A[i].x),A[i].id=i;
sort(A+1,A+1+n);
build(root[1],1,n);
for (int i=2;i<=n;i++){
root[i]=root[i-1]; //以第i大作为中位数
insert(root[i],1,n,A[i-1].id);
}
lastans=0;
scanf("%d",&m);
int a,b,c,d;
for (int i=1;i<=m;i++)
{
scanf("%d%d%d%d",&a,&b,&c,&d);
q[1]=(a+lastans)%n; q[2]=(b+lastans)%n; q[3]=(c+lastans)%n; q[4]=(d+lastans)%n;
sort(q+1,q+5);
a=q[1]+1; b=q[2]+1; c=q[3]+1; d=q[4]+1;
solve(a,b,c,d);
}
return 0;
}