题目描述
题解
可以发现所有满足条件的中位数是具有单调性的,也就是说,如果
M′<M
,且
M
为合法的中位数,那么
这里利用了一个非常厉害的技巧:根据题目中对中位数的定义,我们发现如果序列长度为奇数,中位数即为最中间的那个数,如果序列长度为偶数,那么中位数为中间的两个数更靠后的那个数。那么这样的话,假设我们要判定
M
是否为合法的中位数,只需要将
怎样维护区间最大连续子序列和呢?实际上线段树就可以实现,只需要维护
sum,maxl,maxr
分别表示区间和,从左端起最大连续子序列和,从右端起最大连续子序列和。那么满足条件
[a,b][c,d]
的最长连续子序列和即为
sum(b,c)+maxl(a,b)+maxr(c,d)
。
可是如果每一次二分都重构线段树的话时间是无法承受的,但是我们想到从
1
到
最坏情况下时间复杂度
O(nlog3n)
非常好的一道思路题,被Yveh教做人。
代码
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
#define N 20005
int n,Q,a,b,c,d,ans,sz;
struct hp{int val,id;}s[N];
int root[N],q[5];
int sum[N*20],lmax[N*20],rmax[N*20],ls[N*20],rs[N*20];
int cmp(hp a,hp b)
{
return a.val<b.val;
}
void update(int now)
{
sum[now]=sum[ls[now]]+sum[rs[now]];
lmax[now]=max(lmax[ls[now]],sum[ls[now]]+lmax[rs[now]]);
rmax[now]=max(rmax[rs[now]],sum[rs[now]]+rmax[ls[now]]);
}
void build(int &now,int l,int r)
{
int mid=(l+r)>>1;now=++sz;
if (l==r)
{
sum[now]=lmax[now]=rmax[now]=1;
return;
}
build(ls[now],l,mid);
build(rs[now],mid+1,r);
update(now);
}
void change(int &now,int l,int r,int x)
{
int mid=(l+r)>>1;
sum[++sz]=sum[now],ls[sz]=ls[now],rs[sz]=rs[now];now=sz;
if (l==r)
{
sum[now]=-1;lmax[now]=max(0,sum[now]);rmax[now]=max(0,sum[now]);
return;
}
if (x<=mid) change(ls[now],l,mid,x);
else change(rs[now],mid+1,r,x);
update(now);
}
int query_s(int now,int l,int r,int lrange,int rrange)
{
if (lrange>rrange) return 0;
int mid=(l+r)>>1,ans=0;
if (lrange<=l&&r<=rrange) return sum[now];
if (lrange<=mid) ans+=query_s(ls[now],l,mid,lrange,rrange);
if (mid+1<=rrange) ans+=query_s(rs[now],mid+1,r,lrange,rrange);
return ans;
}
int query_r(int now,int l,int r,int lrange,int rrange)
{
if (lrange>rrange) return 0;
int mid=(l+r)>>1,ans=0;
if (lrange<=l&&r<=rrange) return rmax[now];
if (mid+1<=rrange)
ans=query_r(rs[now],mid+1,r,lrange,rrange);
if (lrange<=mid)
{
int l_maxr=query_r(ls[now],l,mid,lrange,rrange);
int r_sum=query_s(rs[now],mid+1,r,mid+1,rrange);
ans=max(ans,l_maxr+r_sum);
}
return ans;
}
int query_l(int now,int l,int r,int lrange,int rrange)
{
if (lrange>rrange) return 0;
int mid=(l+r)>>1,ans=0;
if (lrange<=l&&r<=rrange) return lmax[now];
if (lrange<=mid)
ans=query_l(ls[now],l,mid,lrange,rrange);
if (mid+1<=rrange)
{
int r_maxl=query_l(rs[now],mid+1,r,lrange,rrange);
int l_sum=query_s(ls[now],l,mid,lrange,mid);
ans=max(ans,r_maxl+l_sum);
}
return ans;
}
bool check(int mid)
{
int ab=query_r(root[mid],1,n,a,b-1);
int cd=query_l(root[mid],1,n,c+1,d);
int bc=query_s(root[mid],1,n,b,c);
if (ab+bc+cd>=0) return true;
else return false;
}
int find()
{
int l=1,r=n,mid,ans=0;
while (l<=r)
{
mid=(l+r)>>1;
if (check(mid)) ans=mid,l=mid+1;
else r=mid-1;
}
return ans;
}
int main()
{
scanf("%d",&n);
for (int i=1;i<=n;++i) scanf("%d",&s[i].val),s[i].id=i;
sort(s+1,s+n+1,cmp);
build(root[1],1,n);
for (int i=2;i<=n;++i)
{
root[i]=root[i-1];
change(root[i],1,n,s[i-1].id);
}
scanf("%d",&Q);
for (int i=1;i<=Q;++i)
{
scanf("%d%d%d%d",&a,&b,&c,&d);
q[0]=(a+ans)%n,q[1]=(b+ans)%n,q[2]=(c+ans)%n,q[3]=(d+ans)%n;
sort(q,q+4);
a=q[0]+1,b=q[1]+1,c=q[2]+1,d=q[3]+1;
ans=s[find()].val;
printf("%d\n",ans);
}
}
总结
①中位数的套路还是很深啊。