测试地址:middle
做法: 本题需要用到二分答案+主席树。
神仙题。很久以前Mychael大佬称这题和TJOI那个排序差不多,于是二分答案那一步想得特别顺畅,结果却卡在简单主席树…菜爆,估计退役稳了。
首先看到这个题和数的大小有很大关联,想到二分答案,那么现在要解决的问题就是,如何判断最大的中位数和
的大小关系?
我们发现,若存在一个满足题目要求的区间,使得区间中
的数的个数,大于等于区间中
的数的个数,那么显然这个区间中的中位数就
,那么最大中位数肯定也
。反之,如果不存在这样的区间,那么就不存在中位数
,所以最大中位数就
。
这时候我们就想,贪心地选一个
的数的数量
的数的数量最大的区间进行判断就可以了,因此我们把
的数标作
,把
的数标作
,现在我们就是要找到满足要求的子段的最大子段和。注意到
中的数无论如何都会被选到,因此只要对
求最大后缀子段和,对
求最大前缀子段和,再加起来即可,显然可以用线段树维护。
上面我们似乎已经非常完美地解决了这个问题,可惜如果每次都这样做的话,一次询问时间复杂度就为
,难以接受(我自己想的时候就卡在这个位置了)。然而注意到,随着
的增大,
会按照原数从小到大的顺序变成
,且只会变一次,于是我们想到预处理出所有可能时刻的线段树,因为变化只有可能是单点修改(一个
变成
),用主席树存储即可,这样一来在每次询问时,就只需要二分出应该在哪棵线段树上走即可,那么我们就做到了
的总时间复杂度,可以通过此题。
以下是本人代码:
#include <bits/stdc++.h>
using namespace std;
const int inf=2000000000;
int n,q,a[20010],tot=0;
struct forsort
{
int id,val;
}f[20010];
int ch[600010][2]={0},rt[20010];
int mxl[600010],mxr[600010],sum[600010];
int ans,xsum;
void pushup(int v)
{
sum[v]=sum[ch[v][0]]+sum[ch[v][1]];
mxl[v]=max(mxl[ch[v][0]],mxl[ch[v][1]]+sum[ch[v][0]]);
mxr[v]=max(mxr[ch[v][1]],mxr[ch[v][0]]+sum[ch[v][1]]);
}
void buildtree(int &v,int l,int r)
{
v=++tot;
if (l==r)
{
sum[v]=1;
mxl[v]=mxr[v]=1;
return;
}
int mid=(l+r)>>1;
buildtree(ch[v][0],l,mid);
buildtree(ch[v][1],mid+1,r);
pushup(v);
}
void modify(int &v,int last,int l,int r,int x)
{
v=++tot;
ch[v][0]=ch[last][0];
ch[v][1]=ch[last][1];
if (l==r)
{
sum[v]=-1;
mxl[v]=mxr[v]=-1;
return;
}
int mid=(l+r)>>1;
if (x<=mid) modify(ch[v][0],ch[last][0],l,mid,x);
else modify(ch[v][1],ch[last][1],mid+1,r,x);
pushup(v);
}
void updateans(int v,bool type)
{
if (!type) ans=max(ans,mxr[v]+xsum);
else ans=max(ans,mxl[v]+xsum);
xsum+=sum[v];
}
void query(int v,int l,int r,int s,int t,bool type)
{
if (l>=s&&r<=t)
{
updateans(v,type);
return;
}
int mid=(l+r)>>1;
if (type)
{
if (s<=mid) query(ch[v][0],l,mid,s,t,type);
if (t>mid) query(ch[v][1],mid+1,r,s,t,type);
}
else
{
if (t>mid) query(ch[v][1],mid+1,r,s,t,type);
if (s<=mid) query(ch[v][0],l,mid,s,t,type);
}
}
bool check(int a,int b,int c,int d,int x)
{
int l=0,r=n;
while(l<r)
{
int mid=(l+r)>>1;
if (f[mid+1].val>=x) r=mid;
else l=mid+1;
}
int totans=0;
if (b<c-1)
{
ans=-inf,xsum=0;
query(rt[l],0,n-1,b+1,c-1,0);
totans+=xsum;
}
ans=-inf,xsum=0;
query(rt[l],0,n-1,a,b,0);
totans+=ans;
ans=-inf,xsum=0;
query(rt[l],0,n-1,c,d,1);
totans+=ans;
return totans>=0;
}
bool cmp(forsort a,forsort b)
{
return a.val<b.val;
}
int main()
{
int l=0,r=0;
scanf("%d",&n);
for(int i=0;i<n;i++)
{
scanf("%d",&a[i]);
r=max(r,a[i]);
f[i+1].id=i,f[i+1].val=a[i];
}
sort(f+1,f+n+1,cmp);
buildtree(rt[0],0,n-1);
for(int i=1;i<=n;i++)
modify(rt[i],rt[i-1],0,n-1,f[i].id);
scanf("%d",&q);
int lastans=0;
for(int i=1;i<=q;i++)
{
int op[5];
for(int j=0;j<4;j++)
{
scanf("%d",&op[j]);
op[j]=(op[j]+lastans)%n;
}
sort(op,op+4);
int L=l,R=r;
while(L<R)
{
int mid=(L+R)>>1;
if (check(op[0],op[1],op[2],op[3],mid+1))
L=mid+1;
else R=mid;
}
printf("%d\n",lastans=L);
}
return 0;
}