bzoj2653: middle
Posted Izaya
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了bzoj2653: middle相关的知识,希望对你有一定的参考价值。
题意:给n个数,每次询问a,b,c,d,你要选定一个区间使得该区间中位数最大,其中a,b为区间左端点可选范围,c,d同理。
OTZ陈老师出的神题。
先考虑一个简单问题:只有一个询问的情况。此时我们二分中位数,并且将区间内小中位数的数标为-1,大于的标为1,此时区间最大和如果大等0,则说明中位数可以变大,然后二分下去就可以了。
加上询问之后,我们就要维护这样一个区间最大和了。考虑主席树,我们对于每一个值,建出它对应的1,-1树,然后二分,到对应的树上去求最大区间和就好了。那么问题来了,怎么维护这样一个最大区间和呢?首先,我们有必选区间b,c所以我要把这个区间的和算上,对于可选区间a,b-1和c+1,d,我们要求一个最大前后缀和。这个东西可以这样求(以前缀和为例):max(左儿子sum+右儿子前缀,左儿子前缀)。
那么整体思路出来了:先建出一棵全线段树(全为1),然后我们把原序列排个序(要把下标对应好),然后一个一个按顺序丢进树里,把小等自己的变为-1,以供这个数的后一个数查询时使用。 然后我们二分答案,对于当前答案去树上找最大区间和,如果大等0则满足条件。
这道题主席树建出来,第一维度是权值,第二维度是下标。我一开始想的一二唯独是反的,而大神说这样有问题,一直没想通哪里有问题。。。再去和大神讨论一下。
写这道题写得异常艰难。先是在想二分会不会有问题(实际是不会的,因为题目弄出来的中位数在偶数个时会选择较大的那个)昨天晚上写了第一版,有点问题,第二天写了第二版,还是有问题,什么没有排序导致没有正确性啊,建树不分配编号啊什么的。然后各种改,终于过了(感谢ihopenot大佬)。
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define N 20005 4 #define M 400005 5 #define CT Chairman_Tree 6 int n,Q,p[5],ans; 7 struct data{ 8 int num,pos; 9 bool operator < (const data& w)const{ 10 if(num==w.num) return pos<w.pos; 11 return num<w.num; 12 } 13 }a[N]; 14 namespace Chairman_Tree{ 15 struct node{ 16 int son[2],sum,ls,rs; 17 }tr[M]; 18 int sz,root[N]; 19 void build(int& x,int l,int r){ 20 x=++sz; 21 tr[x].ls=tr[x].rs=tr[x].sum=r-l+1; 22 if(l==r) return; 23 int mid=(l+r)>>1; 24 build(tr[x].son[0],l,mid); 25 build(tr[x].son[1],mid+1,r); 26 } 27 void insert(int x,int& y,int l,int r,int lim){ 28 tr[y=++sz].sum=tr[x].sum-2; 29 if(l==r){ 30 tr[y].ls=tr[y].rs=0; 31 return; 32 } 33 memcpy(tr[y].son,tr[x].son,sizeof(tr[y].son)); 34 int mid=(l+r)>>1,LS,RS; 35 if(lim>mid) insert(tr[x].son[1],tr[y].son[1],mid+1,r,lim); 36 else insert(tr[x].son[0],tr[y].son[0],l,mid,lim); 37 LS=tr[y].son[0],RS=tr[y].son[1]; 38 tr[y].ls=max(tr[LS].sum+tr[RS].ls,tr[LS].ls); 39 tr[y].rs=max(tr[RS].sum+tr[LS].rs,tr[RS].rs); 40 } 41 inline void insert(int i) {insert(root[i],root[i+1],1,n,a[i].pos);} 42 int query(int x,int L,int R,int l,int r){ 43 if(L==l && R==r) return tr[x].sum; 44 int mid=(L+R)>>1; 45 if(r<=mid) return query(tr[x].son[0],L,mid,l,r); 46 else if(mid<l) return query(tr[x].son[1],mid+1,R,l,r); 47 else return query(tr[x].son[0],L,mid,l,mid)+query(tr[x].son[1],mid+1,R,mid+1,r); 48 } 49 int lquery(int x,int L,int R,int l,int r){ 50 if(L==l && R==r) return tr[x].ls; 51 int mid=(L+R)>>1; 52 if(r<=mid) return lquery(tr[x].son[0],L,mid,l,r); 53 else if(mid<l) return lquery(tr[x].son[1],mid+1,R,l,r); 54 else { 55 int t1=query(tr[x].son[0],L,mid,l,mid)+lquery(tr[x].son[1],mid+1,R,mid+1,r),t2=lquery(tr[x].son[0],L,mid,l,mid); 56 return max(t1,t2); 57 } 58 } 59 int rquery(int x,int L,int R,int l,int r){ 60 if(L==l && R==r) return tr[x].rs; 61 int mid=(L+R)>>1; 62 if(r<=mid) return rquery(tr[x].son[0],L,mid,l,r); 63 else if(mid<l) return rquery(tr[x].son[1],mid+1,R,l,r); 64 else { 65 int t1=rquery(tr[x].son[0],L,mid,l,mid)+query(tr[x].son[1],mid+1,R,mid+1,r),t2=rquery(tr[x].son[1],mid+1,R,mid+1,r); 66 return max(t1,t2); 67 } 68 } 69 } 70 using namespace CT; 71 inline int read(){ 72 int x=0,f=1; char a=getchar(); 73 while(a<‘0‘ || a>‘9‘) {if(a==‘-‘) f=-1; a=getchar();} 74 while(a>=‘0‘ && a<=‘9‘) x=x*10+a-‘0‘,a=getchar(); 75 return x*f; 76 } 77 inline bool jud(int x){ 78 return rquery(root[x],1,n,p[1],p[2]-1)+query(root[x],1,n,p[2],p[3])+lquery(root[x],1,n,p[3]+1,p[4])>=0; 79 } 80 int main(){ 81 n=read(); 82 for(int i=1;i<=n;i++) a[i]=(data){read(),i}; 83 build(root[1],1,n); 84 sort(a+1,a+1+n); 85 for(int i=1;i<n;i++) insert(i); 86 Q=read(); 87 while(Q--){ 88 int l=1,r=n; 89 for(int i=1;i<=4;i++) p[i]=(read()+ans)%n+1; 90 sort(p+1,p+5); 91 while(l<r){ 92 int mid=(l+r+1)>>1; 93 if(jud(mid)) l=mid; 94 else r=mid-1; 95 } 96 ans=a[l].num; printf("%d\n",ans); 97 } 98 return 0; 99 }
以上是关于bzoj2653: middle的主要内容,如果未能解决你的问题,请参考以下文章