BZOJ 2653: middle
2653: middle
Time Limit: 20 Sec Memory Limit: 512 MBSubmit: 1536 Solved: 855
[Submit][Status][Discuss]
Description
一个长度为n的序列a,设其排过序之后为b,其中位数定义为b[n/2],其中a,b从0开始标号,除法取下整。
给你一个长度为n的序列s。
回答Q个这样的询问:s的左端点在[a,b]之间,右端点在[c,d]之间的子序列中,最大的中位数。
其中a<b<c<d。
位置也从0开始标号。
我会使用一些方式强制你在线。
Input
第一行序列长度n。
接下来n行按顺序给出a中的数。
接下来一行Q。
然后Q行每行a,b,c,d,我们令上个询问的答案是x(如果这是第一个询问则x=0)。
令数组q={(a+x)%n,(b+x)%n,(c+x)%n,(d+x)%n}。
将q从小到大排序之后,令真正的要询问的a=q[0],b=q[1],c=q[2],d=q[3]。
输入保证满足条件。
Output
Q行依次给出询问的答案。
Sample Input
170337785
271451044
22430280
969056313
206452321
3
3 1 0 2
2 3 1 4
3 1 4 0
271451044
271451044
969056313
Sample Output
HINT
0:n,Q<=100
1,...,5:n<=2000
0,...,19:n<=20000,Q<=25000
Source
分析:
首先我们大概都能够想到二分答案然后判断...
怎么判断?一个很经典的思想,我才不会告诉你我没有想到QAQ...
对于一个序列S,设其中位数位M,二分答案位ans,如果M>=ans,那么一定满足S中有大于等于$\frac{|S|}{2}$的数字大于等于ans,所以我们需要在给顶序列中寻找合法的子序列使得存在M>=ans,我们可以把大于等于ans的数字置成1,小于ans的数字置为0,然后问题就转化为了在合法序列中寻找最大子段和使得其大于等于0...这就是基础的线段树问题,但是这样我们就需要n个线段树,很显然这是不兹磁的...所以我们先把ans=1(离散化之后)的线段树建出来,也就是说每个位置都是1,然后对于以后的i,用可持久化线段树单点修改压空间...查询的时候就在对应的线段树上查询即可...
代码:
不要问我为什么我要手写lower_bound还写得这么鬼畜...因为我开心...调都没调就A了...
#include<algorithm> #include<iostream> #include<cstring> #include<cstdio> #include<vector> //by NeighThorn using namespace std; const int maxn=20000+5,maxm=7000000+5; int n,q,len,tot,ls[maxm],rs[maxm],sum[maxm],lmax[maxm],rmax[maxm],root[maxn]; long long a[maxn],mp[maxn]; vector<int> M[maxn]; inline int find(long long _){ int __=1,___=len,____; while(__<=___){ int _____=(__+___)>>1; if(mp[_____]>=_) ____=_____,___=_____-1; else __=_____+1; } return ____; } inline void build(int l,int r,int &x){ x=++tot; if(l==r){ lmax[x]=rmax[x]=sum[x]=1; return; } int mid=(l+r)>>1; build(l,mid,ls[x]),build(mid+1,r,rs[x]); sum[x]=sum[ls[x]]+sum[rs[x]]; lmax[x]=max(lmax[ls[x]],sum[ls[x]]+lmax[rs[x]]); rmax[x]=max(rmax[rs[x]],sum[rs[x]]+rmax[ls[x]]); } inline void change(int l,int r,int x,int &y,int pos,int val){ y=++tot; if(l==r){ lmax[y]=rmax[y]=sum[y]=val; return; } int mid=(l+r)>>1;ls[y]=ls[x],rs[y]=rs[x]; if(pos<=mid) change(l,mid,ls[x],ls[y],pos,val); else change(mid+1,r,rs[x],rs[y],pos,val); sum[y]=sum[ls[y]]+sum[rs[y]]; lmax[y]=max(lmax[ls[y]],sum[ls[y]]+lmax[rs[y]]); rmax[y]=max(rmax[rs[y]],sum[rs[y]]+rmax[ls[y]]); } inline int querysum(int l,int r,int L,int R,int x){ if(l==L&&r==R) return sum[x]; int mid=(l+r)>>1; if(R<=mid) return querysum(l,mid,L,R,ls[x]); else if(L>mid) return querysum(mid+1,r,L,R,rs[x]); else return querysum(l,mid,L,mid,ls[x])+querysum(mid+1,r,mid+1,R,rs[x]); } inline int queryrmax(int l,int r,int L,int R,int x){ if(l==L&&r==R) return rmax[x]; int mid=(l+r)>>1; if(R<=mid) return queryrmax(l,mid,L,R,ls[x]); else if(L>mid) return queryrmax(mid+1,r,L,R,rs[x]); else return max(queryrmax(l,mid,L,mid,ls[x])+querysum(mid+1,r,mid+1,R,rs[x]),queryrmax(mid+1,r,mid+1,R,rs[x])); } inline int querylmax(int l,int r,int L,int R,int x){ if(l==L&&r==R) return lmax[x]; int mid=(l+r)>>1,ans; if(R<=mid) return querylmax(l,mid,L,R,ls[x]); else if(L>mid) return querylmax(mid+1,r,L,R,rs[x]); else return max(querylmax(mid+1,r,mid+1,R,rs[x])+querysum(l,mid,L,mid,ls[x]),querylmax(l,mid,L,mid,ls[x])); } signed main(void){ scanf("%d",&n); for(int i=1;i<=n;i++) scanf("%lld",&a[i]),mp[i]=a[i]; sort(mp+1,mp+n+1),len=unique(mp+1,mp+n+1)-mp-1; for(int i=1;i<=n;i++) a[i]=find(a[i]),M[a[i]].push_back(i); build(1,n,root[1]); for(int i=2;i<=len;i++) if(M[i-1].size()>0){ change(1,n,root[i-1],root[i],M[i-1][0],-1); for(int j=0;j<M[i-1].size();j++) change(1,n,root[i],root[i],M[i-1][j],-1); } scanf("%d",&q);long long ans=0,a[4]; for(int i=1;i<=q;i++){ for(int j=0;j<4;j++) scanf("%lld",&a[j]),a[j]=(a[j]+ans)%n,a[j]++; sort(a,a+4); int l=1,r=len; while(l<=r){ int mid=(l+r)>>1; int lm=queryrmax(1,n,a[0],a[1],root[mid]); int s=0; if(a[1]+1<=a[2]-1) s=querysum(1,n,a[1]+1,a[2]-1,root[mid]); int rm=querylmax(1,n,a[2],a[3],root[mid]); if(lm+s+rm>=0) ans=mid,l=mid+1; else r=mid-1; }ans=mp[ans]; printf("%d\n",ans); } return 0; }
By NeighThorn