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
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
题解:
这道题是基于这么个idea,如何在不求出一个数列a的中位数的情况下,判断数x是大于等于中位数
我们可以考虑定义一个新数列b,满足b[i]= a[i]>=x?1:-1 ,如果b数列的总和大于等于0,则数x小于等于中位数,否则反之
然后这道题我们可以二分一个数x,判断是否能成为中位数,我们可以将大于等于x的数看成1,小于的看成-1
查询左端点在 [ a , b ] , 右端点在 [ c , d ] 的最大子段和,如果最大子段和大于等于0,说明x小于等于中位数,否则反之
但现在x是变化的,我们显然不能每次暴力把某个数改成1或-1
显然x只能是原序列的数
将原序列的数离散化一下后,此时x的取值范围显然是1到n,我们发现当x变成x+1时,只有第x个数由+1变为-1
而对于某个序列的最大子段和显然可以用线段树维护,而每次只会修改一个数
我们就可以用可持久化线段树把历史上每个线段树都建出来,然后在第x个上进行查询
总的时间复杂度为O( n log 2 n )
code:
1 #include<cstdio> 2 #include<iostream> 3 #include<cmath> 4 #include<cstring> 5 #include<algorithm> 6 using namespace std; 7 char ch; 8 bool ok; 9 void read(int &x){ 10 for (ok=0,ch=getchar();!isdigit(ch);ch=getchar()) if (ch=='-') ok=1; 11 for (x=0;isdigit(ch);x=x*10+ch-'0',ch=getchar()); 12 if (ok) x=-x; 13 } 14 const int maxn=20005; 15 const int maxnode=310000; 16 const int inf=0x7f7f7f7f; 17 int n,q,t[4],ans; 18 struct Data{ 19 int v,id; 20 void init(int i){read(v),id=i;} 21 }list[maxn]; 22 bool cmp(const Data &a,const Data &b){return a.v<b.v;} 23 struct Node{ 24 int lmax,rmax,maxv,sum; 25 void init(int v){lmax=rmax=maxv=sum=v;} 26 }t1,t2,t3; 27 Node operator+(const Node &x,const Node &y){ 28 return (Node){max(x.lmax,x.sum+y.lmax),max(y.rmax,y.sum+x.rmax),max(x.rmax+y.lmax,max(x.maxv,y.maxv)),x.sum+y.sum}; 29 } 30 struct seg{ 31 int tot,root[maxn],son[maxnode][2]; 32 Node node[maxnode]; 33 void build(int &k,int l,int r){ 34 k=++tot; 35 if (l==r){node[k].init(1);return;} 36 int m=(l+r)>>1; 37 build(son[k][0],l,m),build(son[k][1],m+1,r),node[k]=node[son[k][0]]+node[son[k][1]]; 38 } 39 void modify(int &k,int p,int l,int r,int x){ 40 k=++tot; 41 if (l==r){node[k].init(-1);return;} 42 int m=(l+r)>>1; 43 if (x<=m) son[k][1]=son[p][1],modify(son[k][0],son[p][0],l,m,x); 44 else son[k][0]=son[p][0],modify(son[k][1],son[p][1],m+1,r,x); 45 node[k]=node[son[k][0]]+node[son[k][1]]; 46 } 47 void modify(int id,int x){modify(root[id],root[id-1],1,n,x);} 48 Node query(int k,int l,int r,int x,int y){ 49 if (l==x&&r==y) return node[k]; 50 int m=(l+r)>>1; 51 if (y<=m) return query(son[k][0],l,m,x,y); 52 else if (x<=m) return query(son[k][0],l,m,x,m)+query(son[k][1],m+1,r,m+1,y); 53 else return query(son[k][1],m+1,r,x,y); 54 } 55 Node query(int id,int x,int y){return x<=y?query(root[id],1,n,x,y):(Node){-inf,-inf,-inf,0};} 56 }T; 57 bool check(int id){return T.query(id,t[0],t[1]).rmax+T.query(id,t[1]+1,t[2]-1).sum+T.query(id,t[2],t[3]).lmax>=0;} 58 int solve(){ 59 int l=1,r=n,m; 60 while (l<r){ 61 m=((l+r)>>1)+1; 62 if (check(m)) l=m; else r=m-1; 63 } 64 return list[l].v; 65 } 66 int main(){ 67 read(n); 68 for (int i=1;i<=n;i++) list[i].init(i); 69 sort(list+1,list+n+1,cmp); 70 T.build(T.root[1],1,n); 71 for (int i=1;i<n;i++) T.modify(i+1,list[i].id); 72 for (read(q);q;q--){ 73 for (int i=0;i<4;i++) read(t[i]),t[i]=(t[i]+ans)%n+1; 74 sort(t,t+4),ans=solve(); 75 printf("%d\n",ans); 76 } 77 return 0; 78 }