题面传送门
这道题我们首先会想到处理中位数的常见方法:
二分枚举答案ans,将大于ans的数变为1,小于的则变成-1;如果这个区间的和大于1,则说明中位数比枚举的答案要大,否则要小;
但是这道题的区间并不确定,难道我们要n*n枚举?肯定不行;
观察性质,我们发现,对于区间[b,c],我们肯定要选择,那么先把这个区间的和加入到答案里;
因为题中说选择尽量大的中位数,根据二分时的性质,区间和越大,中位数越大;所以我们所选择的区间的和要最大;
也就是说,我们还要选取rmax[a,b]和lmax[c,d];
综上所述,对于每次二分的判定,如果rmax[a,b]+lmax[c,d]+sum(b,c)>=0,则说明中位数比现在枚举的答案大,然后更改二分条件L=mid+1;
我们可以对于每次二分的答案将区间改成01序列,利用线段树可以做到nlogn;
可是这样空间复杂度原地爆炸,于是想到了我们的好朋友:主席树;
发现:如果二分的答案ans变为ans+1,那么ans+1所对应的线段树上相对于ans的线段树只有等于ans+1的点的权值有所变动,显然每次多需要建的树节点均摊下来只有(logn);
于是开心的用$n\log ^{2}n$的时间复杂度AC掉了这道题;
#include#define inc(i,a,b) for(register int i=a;i<=b;i++) using namespace std; int aa[20010],lisan; int in[10],root[20010]; class node2{ public: int x,id; }query[200010]; bool cmp(node2 x,node2 y){ return x.x<y.x; } class node{ public: int lson,rson; int sum,lmax,rmax; #define mid (l+r)/2 }tree[2000010]; int tot; void updata(int k){ tree[k].sum=tree[tree[k].lson].sum+tree[tree[k].rson].sum; tree[k].lmax=max(tree[tree[k].lson].lmax,tree[tree[k].rson].lmax+tree[tree[k].lson].sum); tree[k].rmax=max(tree[tree[k].rson].rmax,tree[tree[k].lson].rmax+tree[tree[k].rson].sum); } int build(int l,int r){ int root=++tot; if(l==r){ tree[root].sum=tree[root].lmax=tree[root].rmax=1; return root; } tree[root].lson=build(l,mid); tree[root].rson=build(mid+1,r); updata(root); return root; } void add(int &now,int pre,int l,int r,int goal,int value){ now=++tot; tree[now]=tree[pre]; if(l==r){ tree[now].lmax=tree[now].rmax=tree[now].sum=value; return; } if(goal<=mid){ add(tree[now].lson,tree[pre].lson,l,mid,goal,value); } else{ add(tree[now].rson,tree[pre].rson,mid+1,r,goal,value); } updata(now); } int querysum(int now,int l,int r,int x,int y){ if(x>r||y return 0; if(x<=l&&r<=y) return tree[now].sum; return (querysum(tree[now].lson,l,mid,x,y)+querysum(tree[now].rson,mid+1,r,x,y)); } int querylmax(int now,int l,int r,int x,int y){ if(x>r||y return -99999999; if(x<=l&&r<=y) return tree[now].lmax; return max(querylmax(tree[now].lson,l,mid,x,y),querylmax(tree[now].rson,mid+1,r,x,y)+querysum(tree[now].lson,l,mid,x,y)); } int queryrmax(int now,int l,int r,int x,int y){ if(x>r||y return -99999999; if(x<=l&&r<=y) return tree[now].rmax; int tmp1=queryrmax(tree[now].rson,mid+1,r,x,y),tmp2=queryrmax(tree[now].lson,l,mid,x,y)+querysum(tree[now].rson,mid+1,r,x,y); return max(tmp1,tmp2); } int n; bool check(int now,int a,int b,int c,int d){ int sum=0; if(b+1<=c-1) sum+=querysum(root[now],1,n,b+1,c-1); sum+=queryrmax(root[now],1,n,a,b); sum+=querylmax(root[now],1,n,c,d); if(sum>=0) return 1; else return 0; } int main() { scanf("%d",&n); inc(i,1,n){ scanf("%d",&query[i].x); query[i].id=i; } sort(query+1,query+1+n,cmp); root[1]=build(1,n); inc(i,2,n+1){ add(root[i],root[i-1],1,n,query[i-1].id,-1); } int q; scanf("%d",&q); int ans=0; inc(i,1,q){ scanf("%d%d%d%d",&in[0],&in[1],&in[2],&in[3]); in[0]=(in[0]+ans)%n; in[1]=(in[1]+ans)%n; in[2]=(in[2]+ans)%n; in[3]=(in[3]+ans)%n; sort(in,in+4); int L=1,R=n; while(L<=R){ int midd=(L+R)/2; if(check(midd,in[0]+1,in[1]+1,in[2]+1,in[3]+1)){ L=midd+1; ans=midd; } else{ R=midd-1; } } ans=query[ans].x; printf("%d\n",ans); } } /* 5 170337785 271451044 22430280 969056313 206452321 3 3 1 0 2 2 3 1 4 3 1 4 0 */