bzoj2653
middle
一个长度为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]。
输入保证满足条件。
第一行所谓“排过序”指的是从小到大排序!
n<=20000,Q<=25000
Output
Q行依次给出询问的答案。
Sample Input
5
170337785 271451044 22430280 969056313 206452321
3
3 1 0 2
2 3 1 4
3 1 4 0
Sample Output
271451044
271451044
969056313
sol : 一个区间的中位数要求这段区间比他大的个数>=比他小的个数,所以我们可以二分答案,对于每个数字建一颗线段树,如[a,b]和[c,d]中的那段区间,就是所有数字中比他大的减去比他小的,在减去[a,b]中的前缀min和[c,d]中的后缀min,但是对于每次都重建一棵树肯定会T出shi,所以弄棵主席树可持久化,先建一个全-1的树,然后按数值大小从大到小做,一次把一个-1改成1,相当于修改一个区间,比如修改位置P,就是改掉[1,P]的后缀min和[P,n]的前缀min
Ps:实现的时候犯错调了好久,详见注释
#include <bits/stdc++.h> using namespace std; typedef long long ll; inline ll read() { ll s=0; bool f=0; char ch=' '; while(!isdigit(ch)) { f|=(ch=='-'); ch=getchar(); } while(isdigit(ch)) { s=(s<<3)+(s<<1)+(ch^48); ch=getchar(); } return (f)?(-s):(s); } #define R(x) x=read() inline void write(ll x) { if(x<0) { putchar('-'); x=-x; } if(x<10) { putchar(x+'0'); return; } write(x/10); putchar((x%10)+'0'); return; } #define W(x) write(x),putchar(' ') #define Wl(x) write(x),putchar('\n') const int N=20005; #define pii pair<int,int> #define fi first #define se second int n,m; struct Shuz { int Num,Pos; inline bool operator<(const Shuz &tmp)const { return Num>tmp.Num; } }A[N]; int rt[N],cnt=0; struct Node { int ls,rs; pii Pre,Suf; }T[N*105]; inline void PushUp(int x) { T[x].Pre.fi=min(T[T[x].ls].Pre.fi,T[T[x].rs].Pre.fi); T[x].Suf.fi=min(T[T[x].ls].Suf.fi,T[T[x].rs].Suf.fi); } inline void PushDown(int x) { if((!T[x].Pre.se)&&(!T[x].Suf.se)) return; int o1=T[x].Pre.se,o2=T[x].Suf.se; int lc=++cnt,rc=++cnt; T[lc]=T[T[x].ls]; T[rc]=T[T[x].rs]; if(o1) { T[lc].Pre.fi+=o1; T[lc].Pre.se+=o1; T[rc].Pre.fi+=o1; T[rc].Pre.se+=o1; } if(o2) { T[lc].Suf.fi+=o2; T[lc].Suf.se+=o2; T[rc].Suf.fi+=o2; T[rc].Suf.se+=o2; } T[x].ls=lc; T[x].rs=rc; T[x].Pre.se=T[x].Suf.se=0; } inline void Build(int &x,int l,int r) { x=++cnt; T[x].Pre.fi=-r; T[x].Pre.se=0; T[x].Suf.fi=-(n-l+1); T[x].Suf.se=0; if(l==r) return; int mid=(l+r)>>1; Build(T[x].ls,l,mid); Build(T[x].rs,mid+1,r); } inline void ChgPre(int &x,int y,int l,int r,int ql,int qr) { x=++cnt; T[x]=T[y]; if(l==ql&&r==qr) { T[x].Pre.fi+=2; T[x].Pre.se+=2; return; } PushDown(y); T[x]=T[y]; //要把y中的标记传下去,否则之后把T[y].ls弄回T[x].ls会萎掉!!! int mid=(l+r)>>1; if(qr<=mid) ChgPre(T[x].ls,T[y].ls,l,mid,ql,qr); else if(ql>mid) ChgPre(T[x].rs,T[y].rs,mid+1,r,ql,qr); else ChgPre(T[x].ls,T[y].ls,l,mid,ql,mid),ChgPre(T[x].rs,T[y].rs,mid+1,r,mid+1,qr); PushUp(x); } inline void ChgSuf(int &x,int y,int l,int r,int ql,int qr) { x=++cnt; T[x]=T[y]; if(l==ql&&r==qr) { T[x].Suf.fi+=2; T[x].Suf.se+=2; return; } PushDown(y); T[x]=T[y]; int mid=(l+r)>>1; if(qr<=mid) ChgSuf(T[x].ls,T[y].ls,l,mid,ql,qr); else if(ql>mid) ChgSuf(T[x].rs,T[y].rs,mid+1,r,ql,qr); else ChgSuf(T[x].ls,T[y].ls,l,mid,ql,mid),ChgSuf(T[x].rs,T[y].rs,mid+1,r,mid+1,qr); PushUp(x); } inline int QueryPre(int x,int l,int r,int ql,int qr) { if(l==ql&&r==qr) return T[x].Pre.fi; int mid=(l+r)>>1; PushDown(x); if(qr<=mid) return QueryPre(T[x].ls,l,mid,ql,qr); else if(ql>mid) return QueryPre(T[x].rs,mid+1,r,ql,qr); else return min(QueryPre(T[x].ls,l,mid,ql,mid),QueryPre(T[x].rs,mid+1,r,mid+1,qr)); PushUp(x); } inline int AskPre(int x,int l,int r,int ql,int qr) { if(qr==1) return 0; if(ql==1) return min(QueryPre(x,l,r,ql,qr-1),0); return QueryPre(x,l,r,ql-1,qr-1); } inline int QuerySuf(int x,int l,int r,int ql,int qr) { if(l==ql&&r==qr) return T[x].Suf.fi; int mid=(l+r)>>1; PushDown(x); if(qr<=mid) return QuerySuf(T[x].ls,l,mid,ql,qr); else if(ql>mid) return QuerySuf(T[x].rs,mid+1,r,ql,qr); else return min(QuerySuf(T[x].ls,l,mid,ql,mid),QuerySuf(T[x].rs,mid+1,r,mid+1,qr)); PushUp(x); } inline int AskSuf(int x,int l,int r,int ql,int qr) { if(ql==n) return 0; if(qr==n) return min(QuerySuf(x,l,r,ql+1,qr),0); return QuerySuf(x,l,r,ql+1,qr+1); } int main() { freopen("2653.in","r",stdin); freopen("2653.out","w",stdout); int i,j,ans=0; R(n); for(i=1;i<=n;i++) A[i]=(Shuz){read(),i}; sort(A+1,A+n+1); // for(i=1;i<=n;i++) cout<<A[i].Num<<' '; puts(""); Build(rt[0],1,n); for(i=1;i<=n;i++) { int tot; ChgPre(tot,rt[i-1],1,n,A[i].Pos,n); ChgSuf(rt[i],tot,1,n,1,A[i].Pos); } R(m); for(i=1;i<=m;i++) { int a,b,c,d,q[5]; for(j=1;j<=4;j++) q[j]=(read()+ans)%n+1; sort(q+1,q+5); a=q[1]; b=q[2]; c=q[3]; d=q[4]; int l=1,r=n,pp; while(l<=r) { int mid=(l+r)>>1; int tmp=mid-(n-mid); //倒序的 if((tmp-AskPre(rt[mid],1,n,a,b)-AskSuf(rt[mid],1,n,c,d))>=0) pp=mid,r=mid-1; //注意是倒序的 else l=mid+1; } Wl(ans=A[pp].Num); // return 0; } return 0; } /* input 5 170337785 271451044 22430280 969056313 206452321 3 3 1 0 2 2 3 1 4 3 1 4 0 Output 271451044 271451044 969056313 */
河田は河田、赤木は赤木……。
私は誰ですか。教えてください、私は誰ですか。
そうだ、俺はあきらめない男、三井寿だ!