[BZOJ]2653: middle
Time Limit: 20 Sec Memory Limit: 512 MB
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
170337785
271451044
22430280
969056313
206452321
3
3 1 0 2
2 3 1 4
3 1 4 0
Sample Output
271451044
271451044
969056313
271451044
969056313
HINT
0:n,Q<=100
1,...,5:n<=2000
0,...,19:n<=20000,Q<=25000
Solution
原题题面有毒,稍微改了改一些会影响到题意理解的描述,如果看不懂BZOJ上的题意就看一看这里的吧……
然后是题解。对每次询问我们考虑二分答案,如果存在一个左端点在[a,b]内,右端点在[c,d]内的连续子序列,满足子序列内大等于当前二分出的答案的数的个数不少于小于该答案的数的个数,那么最大答案一定不会小于该答案,现在假设我们确定了这个答案,我们把大等于该答案的数设为1,小于该答案的数设成-1,那么可以转化为求最大子段和,自然想到用线段树维护这些1和-1的前缀和,用[c,d]中最大的前缀和减去[a-1,b-1]中最小的前缀和,如果大等于0即check成功。但我们不能对每个值都建这样一棵线段树,我们发现第k大的数对应的线段树相比第k+1大的数的线段树,只有一个1变成了-1,于是我们从小到大加入这些数,用可持久化线段树维护区间减(因为我们维护的是前缀和)和区间最值即可,总复杂度O(nlogn+qlogn^2)。
Code
#include<cstdio> #include<algorithm> using namespace std; inline int read() { int x;char c; while((c=getchar())<'0'||c>'9'); for(x=c-'0';(c=getchar())>='0'&&c<='9';)x=x*10+c-'0'; return x; } #define MN 20000 #define ND 900000 struct node{int l,r,mn,mx,mk;}t[ND+5]; int a[MN+5],c[MN+5],rt[MN+5],tn; bool cmp(int x,int y){return a[x]<a[y];} inline void up(int k) { t[k].mn=min(t[t[k].l].mn,t[t[k].r].mn); t[k].mx=max(t[t[k].l].mx,t[t[k].r].mx); } int build(int l,int r) { int p=++tn,mid=l+r>>1; t[p].mn=l;t[p].mx=r; if(l<r)t[p].l=build(l,mid),t[p].r=build(mid+1,r); return p; } int mark(int k,int x) { t[++tn].l=t[k].l;t[tn].r=t[k].r; t[tn].mn=t[k].mn+x;t[tn].mx=t[k].mx+x;t[tn].mk=t[k].l?t[k].mk+x:0; return tn; } int down(int k) { int p=++tn; t[p].l=t[k].mk?mark(t[k].l,t[k].mk):t[k].l; t[p].r=t[k].mk?mark(t[k].r,t[k].mk):t[k].r; t[p].mn=t[k].mn;t[p].mx=t[k].mx; return p; } int dec(int k,int l,int r,int L) { int p=down(k),mid=l+r>>1; if(l==L)return mark(p,-2); if(L>mid)t[p].r=dec(t[p].r,mid+1,r,L); else t[p].l=dec(t[p].l,l,mid,L),t[p].r=dec(t[p].r,mid+1,r,mid+1); return up(p),p; } int qmn(int k,int l,int r,int L,int R,int mk) { if(l==L&&r==R)return t[k].mn+mk; int mid=l+r>>1;mk+=t[k].mk; if(R<=mid)return qmn(t[k].l,l,mid,L,R,mk); if(L>mid)return qmn(t[k].r,mid+1,r,L,R,mk); return min(qmn(t[k].l,l,mid,L,mid,mk),qmn(t[k].r,mid+1,r,mid+1,R,mk)); } int qmx(int k,int l,int r,int L,int R,int mk) { if(l==L&&r==R)return t[k].mx+mk; int mid=l+r>>1;mk+=t[k].mk; if(R<=mid)return qmx(t[k].l,l,mid,L,R,mk); if(L>mid)return qmx(t[k].r,mid+1,r,L,R,mk); return max(qmx(t[k].l,l,mid,L,mid,mk),qmx(t[k].r,mid+1,r,mid+1,R,mk)); } int main() { int n=read(),i,j,ans,ls=0,q[4]; for(i=1;i<=n;++i)a[c[i]=i]=read(); sort(c+1,c+n+1,cmp); rt[1]=build(0,n); for(i=2;i<=n;++i)rt[i]=dec(rt[i-1],0,n,c[i-1]); for(i=read();i--;) { for(j=0;j<4;++j)q[j]=(read()+ls)%n+1;sort(q,q+4); for(int l=1,r=n,mid;l<=r;) { mid=l+r>>1; if(qmx(rt[mid],0,n,q[2],q[3],0) -qmn(rt[mid],0,n,q[0]-1,q[1]-1,0)<0)r=mid-1; else ans=mid,l=mid+1; } printf("%d\n",ls=a[c[ans]]); } }