Problem
Solution
这道题思路很神,感觉二分答案题愈发得神了起来。
我们考虑如果选择x作为中位数,把序列中所有小于x的数标记为-1,大于的标记为1,那么询问x能否作为一段区间的中位数,就相当于询问是否存在一个区间使得区间和是大于等于0的。显然我们可以用线段树维护出一个最大区间和,那么直接查它是否大于等于0即可。而x是可以二分的。
除此之外对于每个数作为中位数,我们都需要一棵这样的线段树,可以用主席树做。
然后方便起见你也可以只写一个query,用结构体存返回值lmax,rmax,maxsum,每次合并答案即可。
时间复杂度
Code
#include <algorithm>
#include <cstdio>
using namespace std;
const int maxn=25010,maxm=1000010,INF=0x3f3f3f3f;
struct data{
int l,r,sum;
void clear(){l=r=-INF;sum=0;}
}res,t[maxm];
int n,m,tot,l,r,mid,ans,a[maxn],id[maxn],opr[5];
int rt[maxn],lc[maxm],rc[maxm];
inline bool cmp(const int &x,const int &y){return a[x]<a[y];}
data merge(data l,data r)
{
static data res;
res.l=max(l.l,l.sum+r.l);res.r=max(r.r,r.sum+l.r);
res.sum=l.sum+r.sum;
return res;
}
void build(int l,int r,int& rt)
{
rt=++tot;
t[rt].l=t[rt].r=t[rt].sum=r-l+1;
if(l==r) return ;
int m=(l+r)>>1;
build(l,m,lc[rt]);build(m+1,r,rc[rt]);
}
void update(int l,int r,int pos,int val,int& rt)
{
t[++tot]=t[rt];lc[tot]=lc[rt];rc[tot]=rc[rt];rt=tot;
if(l==r){t[rt].l=t[rt].r=t[rt].sum=val;return ;}
int m=(l+r)>>1;
if(pos<=m) update(l,m,pos,val,lc[rt]);
else update(m+1,r,pos,val,rc[rt]);
t[rt]=merge(t[lc[rt]],t[rc[rt]]);
}
void query(int l,int r,int L,int R,int rt)
{
if(L<=l&&r<=R){res=merge(res,t[rt]);return ;}
int m=(l+r)>>1;
if(L<=m) query(l,m,L,R,lc[rt]);
if(m<R) query(m+1,r,L,R,rc[rt]);
}
int check(int k)
{
int ret=0;
if(opr[2]+1<=opr[3]-1)
{
res.clear();
query(1,n,opr[2]+1,opr[3]-1,rt[k]);
ret+=res.sum;
}
res.clear();query(1,n,opr[1],opr[2],rt[k]);ret+=res.r;
res.clear();query(1,n,opr[3],opr[4],rt[k]);ret+=res.l;
return ret>=0;
}
int main()
{
#ifndef ONLINE_JUDGE
freopen("in.txt","r",stdin);
#endif
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%d",&a[i]),id[i]=i;
sort(id+1,id+n+1,cmp);build(1,n,rt[1]);
for(int i=2;i<=n;i++){rt[i]=rt[i-1];update(1,n,id[i-1],-1,rt[i]);}
scanf("%d",&m);
for(int i=1;i<=m;i++)
{
scanf("%d%d%d%d",&opr[1],&opr[2],&opr[3],&opr[4]);
opr[1]=(opr[1]+ans)%n+1;opr[2]=(opr[2]+ans)%n+1;
opr[3]=(opr[3]+ans)%n+1;opr[4]=(opr[4]+ans)%n+1;
sort(opr+1,opr+5);
l=1;r=n;
while(l<=r)
{
mid=(l+r)>>1;
if(check(mid)) ans=a[id[mid]],l=mid+1;
else r=mid-1;
}
printf("%d\n",ans);
}
return 0;
}