BZOJ2653: middle
2653: middle
Time Limit: 20 Sec Memory Limit: 512 MBSubmit: 3027 Solved: 1731
[Submit][Status][Discuss]
Description
Input
Output
Q行依次给出询问的答案。
Sample Input
170337785
271451044
22430280
969056313
206452321
3
3 1 0 2
2 3 1 4
3 1 4 0
Sample Output
271451044
969056313
HINT
Source
思路{
记得原来做过一道$TJOI$的题目也与数列的部分排序有关,
这种题的一般套路是二分一个答案然后把这个数列根据二分的那个值简化成一个$01$列就很容易利用数据结构维护所需要的信息。
对于这道题,每一问的答案明显满足二分性质。
我们首先二分一个答案x。我们先考虑中位数对于一个简化串的性质。
发现如果$≥$中位数的数变成$1$,$<$中位数的数变成$-1$,那么数列和就一定是$0$或者$1$;
那么我们再做推广,对于某一个特定的数$num$,按照上述方法将这个数列简化,
数列和小于$0$时,说明比它小的数超过了一半,中位数要更小,数列和大于等于$0$时,说明比它大的数超过了一般,中位数要更大。
这就是我们二分的$check$函数的策略。
我们先考虑单次$check$现在我们不知道区间的端点,只知道它肯定包括了一个$[b+1,c-1]$的区间,
于是我们直接用线段树查这一段的区间和记为$sum1$,对于前后的给定区间分别记后缀和$x$和前缀和$y$;
现在我们只需要比较$x+sum1+y$与$0$的关系了,我们做如下分类讨论:
- $x_{max}+sum1+y_{max}<0$:此时肯定不存在满足的区间,所以$r=mid$;
- $x_{min}+sum1+y_{min}>0$:此时所有的给定区间均满足,所以$l=mid$;
- $x_{max}+sum1+y_{max}>0,x_{min}+sum1+y_{min}<0$同时成立,同时我们发现:$x+sum1+y$一定能够取到$x_{max}+sum1+y_{max}$与$x_{min}+sum1+y_{min}>0$中的所有数,那么就一定能够取到$0$,即存在某一个区间中的数以此为中位数,符合题意,$l=mid$;
后两种情况可以合并,那么我们只需要考虑第一种情况是否成立即可,因此我们可以用线段树来维护区间的前缀最大值和后缀最大值了。
处理完这个问题,我们发现还有一个关键的问题没有解决:那就是对于每一个二分出来的$mid$,怎么快速地构建出简化数列呢?
这就需要可持久化了,我们假设按照数列中的每一个数的值$a_i$构建简化数列并建树。
我们发现,将$a_i$排序后的相邻两项对应的线段树实际上只有有限位置上的改变(从$a_i$到$a_{i+1}$只是将$a_i$所在位置上的值变成了$-1$),
也就是说改变了有限条链,于是我们就可以用主席树的思想建树。
按照排序后的值从小到大建树,每次修改某个位置上的值只增加一条链,且一个位置只会被修改一次。
这样,当我们$check$的时候,只需要查询$mid$所对应的这棵线段树上的信息$x_{max}+sum1+y_{max}$就好了。
建树的复杂是$O(nlogn)$(新建初始的线段树和修改$n$个位置上的数也就是增加$n$条长度为$logn$的链)
查询的复杂度则是$O(log^2n)$。
从而很完美地解决了这个问题。
}
#include<bits/stdc++.h> #define ll long long #define db double #define N 20010 #define mid ((l+r)>>1) using namespace std; vector<int>que[N]; int n,a[N],sub[N],idn,sz,bl[N]; struct node{ int sum,lmax,rmax; }tr[N*20]; int ls[N*20],rs[N*20],rt[N]; node up(node a,node b){ return (node){a.sum+b.sum,max(a.sum+b.lmax,a.lmax),max(a.rmax+b.sum,b.rmax)}; } void build(int &x,int l,int r){ x=++idn; if(l==r){tr[x]=(node){1,1,1};return;} build(rs[x],mid+1,r); build(ls[x],l,mid); tr[x]=up(tr[ls[x]],tr[rs[x]]); } void modify(int y,int &x,int k,int l,int r){ x=++idn;tr[x]=tr[y];ls[x]=ls[y],rs[x]=rs[y]; if(l==r){ tr[x]=(node){-1,-1,-1}; return ; } if(k>mid)modify(rs[y],rs[x],k,mid+1,r); else modify(ls[y],ls[x],k,l,mid); tr[x]=up(tr[ls[x]],tr[rs[x]]); } void build_tree(){ build(rt[++rt[0]],1,n);bl[1]=1; for(int i=1;i<sz;++i){ for(int j=0;j<que[i].size();++j) modify(rt[rt[0]],rt[rt[0]+1],que[i][j],1,n),bl[i+1]=++rt[0]; } } int querysum(int x,int l,int r,int L,int R){ if(L>R)return 0; if(l>=L&&r<=R)return tr[x].sum; if(mid<L)return querysum(rs[x],mid+1,r,L,R); if(mid>=R)return querysum(ls[x],l,mid,L,R); return querysum(rs[x],mid+1,r,mid+1,R)+querysum(ls[x],l,mid,L,mid); } int querylmax(int x,int l,int r,int L,int R){ if(l==L&&r==R)return tr[x].lmax; if(mid>=R)return querylmax(ls[x],l,mid,L,R); if(mid<L)return querylmax(rs[x],mid+1,r,L,R); return max(querylmax(ls[x],l,mid,L,mid),querysum(ls[x],l,mid,L,mid)+querylmax(rs[x],mid+1,r,mid+1,R)); } int queryrmax(int x,int l,int r,int L,int R){ if(l==L&&r==R)return tr[x].rmax; if(mid>=R)return queryrmax(ls[x],l,mid,L,R); if(mid<L)return queryrmax(rs[x],mid+1,r,L,R); return max(queryrmax(rs[x],mid+1,r,mid+1,R),querysum(rs[x],mid+1,r,mid+1,R)+queryrmax(ls[x],l,mid,L,mid)); } int q[5]; bool check(int m){ return (querysum(rt[bl[m]],1,n,q[2]+1,q[3]-1)+ queryrmax(rt[bl[m]],1,n,q[1],q[2])+ querylmax(rt[bl[m]],1,n,q[3],q[4]))>=0; } int main(){ freopen("1.in","r",stdin); scanf("%d",&n); for(int i=1;i<=n;++i)scanf("%d",a+i),sub[++sub[0]]=a[i]; sort(sub+1,sub+sub[0]+1); sz=unique(sub+1,sub+sub[0]+1)-sub-1; for(int i=1;i<=n;++i) a[i]=lower_bound(sub+1,sub+sz+1,a[i])-sub,que[a[i]].push_back(i); build_tree(); int T;scanf("%d",&T); int ans=0; while(T--){ for(int i=1;i<=4;++i)scanf("%d",q+i),q[i]=(q[i]+ans)%n+1; sort(q+1,q+5); int l=1,r=sz; while(l<=r){ if(check(mid))l=mid+1; else r=mid-1; } ans=sub[(l+r)>>1]; printf("%d\n",ans); } return 0; }