【BZOJ2653】middle
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
5
170337785
271451044
22430280
969056313
206452321
3
3 1 0 2
2 3 1 4
3 1 4 0
271451044
271451044
969056313
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
题解:好吧这题不看题解还真的很难想~
首先二分中位数还是挺好像的,但问题是怎么判断一个中位数是否可行。一个中位数mid可行的条件是序列中(≥mid的数的个数)≥(<mid的数的个数),也就是说,我们将比≥mid的数看成1,<mid的数看成-1,那么需要存在一段区间,使得区间和非负。这又和可持久化线段树有什么关系呢?
我们将所有数排序,然后令1-n的初值都是1,然后将n个数从小到大扔到可持久化线段树中去,并将对应位置变成-1,这样就很好的满足了所给条件。现在问题就是如何判断[a,b]-[c,d]中有没有符合条件的区间,只需要对线段树维护区间连续子段和,最大连续前缀子段和,最大后缀子段和,然后搞一搞就行了。(小白逛公园的简化)
#include <cstdio> #include <cstring> #include <iostream> #include <algorithm> using namespace std; const int maxn=20010; int n,m,maxx,minn,tot,ans; struct sag { int ls,rs,sum,lm,rm,sm; }s[maxn*1000]; struct node { int num,org; }p[maxn]; int q[10],rt[maxn]; int rd() { int ret=0,f=1; char gc=getchar(); while(gc<'0'||gc>'9') {if(gc=='-')f=-f; gc=getchar();} while(gc>='0'&&gc<='9') ret=ret*10+gc-'0',gc=getchar(); return ret*f; } int max(int a,int b,int c) { return max(max(a,b),c); } void pushup(int x) { s[x].lm=max(s[s[x].ls].lm,s[s[x].ls].sum+s[s[x].rs].lm,0); s[x].rm=max(s[s[x].rs].rm,s[s[x].rs].sum+s[s[x].ls].rm,0); s[x].sm=max(s[s[x].ls].sm,s[s[x].rs].sm,s[s[x].ls].rm+s[s[x].rs].lm); s[x].sum=s[s[x].ls].sum+s[s[x].rs].sum; } void build(int l,int r,int &x) { if(!x) x=++tot; if(l==r) { s[x].sum=s[x].sm=s[x].lm=s[x].rm=1; return; } int mid=l+r>>1; build(l,mid,s[x].ls),build(mid+1,r,s[x].rs); pushup(x); } void insert(int x,int &y,int l,int r,int pos) { if(r<pos) return ; y=++tot; if(l==r) { s[y].sum=-1,s[y].lm=s[y].rm=s[y].sm=0; return ; } int mid=l+r>>1; if(pos<=mid) s[y].rs=s[x].rs,insert(s[x].ls,s[y].ls,l,mid,pos); else s[y].ls=s[x].ls,insert(s[x].rs,s[y].rs,mid+1,r,pos); pushup(y); } bool cmp(node a,node b) { return a.num<b.num; } int qs(int l,int r,int x,int a,int b) { if(a>b) return 0; if(a<=l&&r<=b) return s[x].sum; int mid=l+r>>1; if(b<=mid) return qs(l,mid,s[x].ls,a,b); if(a>mid) return qs(mid+1,r,s[x].rs,a,b); return qs(l,mid,s[x].ls,a,b)+qs(mid+1,r,s[x].rs,a,b); } int ql(int l,int r,int x,int a,int b) { if(a>b) return 0; if(a<=l&&r<=b) return s[x].rm; int mid=l+r>>1; if(b<=mid) return ql(l,mid,s[x].ls,a,b); if(a>mid) return ql(mid+1,r,s[x].rs,a,b); return max(ql(l,mid,s[x].ls,a,b)+qs(mid+1,r,s[x].rs,a,b),ql(mid+1,r,s[x].rs,a,b)); } int qr(int l,int r,int x,int a,int b) { if(a>b) return 0; if(a<=l&&r<=b) return s[x].lm; int mid=l+r>>1; if(b<=mid) return qr(l,mid,s[x].ls,a,b); if(a>mid) return qr(mid+1,r,s[x].rs,a,b); return max(qr(mid+1,r,s[x].rs,a,b)+qs(l,mid,s[x].ls,a,b),qr(l,mid,s[x].ls,a,b)); } int solve(int sta) { int a=ql(1,n,rt[sta-1],q[0],q[1]-1); int b=qs(1,n,rt[sta-1],q[1],q[2]); int c=qr(1,n,rt[sta-1],q[2]+1,q[3]); if(a+b+c>=0) return 1; return 0; } int main() { n=rd(); int i,j,l,r,mid; for(i=1;i<=n;i++) p[i].num=rd(),p[i].org=i,maxx=max(maxx,p[i].num),minn=min(minn,p[i].num); build(1,n,rt[0]); sort(p+1,p+n+1,cmp); for(i=1;i<=n;i++) insert(rt[i-1],rt[i],1,n,p[i].org); m=rd(); for(i=1;i<=m;i++) { for(j=0;j<4;j++) q[j]=(rd()+ans)%n+1; sort(q+0,q+4); l=1,r=n+1; while(l<r) { mid=l+r>>1; if(solve(mid)) l=mid+1; else r=mid; } ans=p[l-1].num; printf("%d ",ans); } return 0; }