题意:给你一个数列,每次询问左端点在[a,b]中,右端点在[c,d]中的所有子区间的中位数最大值?
n<=1e5.
标程:
1 #include<bits/stdc++.h> 2 using namespace std; 3 int read() 4 { 5 int x=0,f=1;char ch=getchar(); 6 while (ch<'0'||ch>'9') {if (ch=='-') f=-1;ch=getchar();} 7 while (ch>='0'&&ch<='9') x=(x<<1)+(x<<3)+ch-'0',ch=getchar(); 8 return x*f; 9 } 10 const int N=800005; 11 const int inf=0x3f3f3f3f; 12 int sc,rm[N],lm[N],ls[N],rs[N],rt[N],q[5],n,a[N],b[N],Q,x,l,r,sum[N]; 13 void up(int k) 14 { 15 sum[k]=sum[ls[k]]+sum[rs[k]]; 16 lm[k]=max(lm[ls[k]],sum[ls[k]]+lm[rs[k]]); 17 rm[k]=max(rm[rs[k]],sum[rs[k]]+rm[ls[k]]); 18 } 19 void build(int &k,int l,int r) 20 { 21 k=++sc; 22 if (l==r) {lm[k]=rm[k]=sum[k]=1;return;} 23 int mid=(l+r)>>1; 24 build(ls[k],l,mid);build(rs[k],mid+1,r); 25 up(k); 26 } 27 void ins(int &k,int pk,int l,int r,int x) 28 { 29 k=++sc;ls[k]=ls[pk];rs[k]=rs[pk]; 30 if (l==r) {lm[k]=rm[k]=sum[k]=-1;return;} 31 int mid=(l+r)>>1; 32 if (x<=mid) ins(ls[k],ls[pk],l,mid,x); 33 else ins(rs[k],rs[pk],mid+1,r,x); 34 up(k); 35 } 36 int qry_sum(int k,int l,int r,int L,int R) 37 { 38 if (L>R) return 0; 39 if (L<=l&&r<=R) return sum[k]; 40 int mid=(l+r)>>1,res=0; 41 if (L<=mid) res+=qry_sum(ls[k],l,mid,L,R); 42 if (R>mid) res+=qry_sum(rs[k],mid+1,r,L,R); 43 return res; 44 } 45 int qry_lm(int k,int l,int r,int L,int R) 46 { 47 if (L==l&&r==R) return lm[k]; 48 int mid=(l+r)>>1; 49 if (L>mid) return qry_lm(rs[k],mid+1,r,L,R); 50 else if (R<=mid) return qry_lm(ls[k],l,mid,L,R); 51 else return max(qry_lm(ls[k],l,mid,L,mid),qry_sum(ls[k],l,mid,L,mid)+qry_lm(rs[k],mid+1,r,mid+1,R)); 52 } 53 int qry_rm(int k,int l,int r,int L,int R) 54 { 55 if (L==l&&r==R) return rm[k]; 56 int mid=(l+r)>>1; 57 if (L>mid) return qry_rm(rs[k],mid+1,r,L,R); 58 else if (R<=mid) return qry_rm(ls[k],l,mid,L,R); 59 else return max(qry_rm(rs[k],mid+1,r,mid+1,R),qry_sum(rs[k],mid+1,r,mid+1,R)+qry_rm(ls[k],l,mid,L,mid)); 60 } 61 bool cmp(int x,int y){return a[x]<a[y];} 62 bool check(int x) 63 { 64 return qry_rm(rt[x],1,n,q[1],q[2])+qry_sum(rt[x],1,n,q[2]+1,q[3]-1)+qry_lm(rt[x],1,n,q[3],q[4])>=0; 65 } 66 int main() 67 { 68 n=read(); 69 for (int i=1;i<=n;i++) a[i]=read(),b[i]=i; 70 sort(b+1,b+n+1,cmp); 71 build(rt[1],1,n); 72 for (int i=2;i<=n;i++) ins(rt[i],rt[i-1],1,n,b[i-1]); 73 Q=read(); 74 while (Q--) 75 { 76 for (int i=1;i<=4;i++) q[i]=(read()+x)%n+1; 77 sort(q+1,q+5); 78 l=1;r=n; 79 while (l<=r) 80 { 81 int mid=(l+r)>>1; 82 if (check(mid)) x=mid,l=mid+1;else r=mid-1; 83 } 84 printf("%d\n",x=a[b[x]]); 85 } 86 return 0; 87 }
易错点:1.强在,忘了把x更新掉。
2.一开始写的是线段树直接维护前缀和。但是这个区间加等差数列比较麻烦,当然也不是不行。
题解:主席树+二分答案
中位数、平均数什么的经典套路就是二分答案。把区间中>=x的设置成1,<x的设置成-1,所以如果有合法区间满足最大区间和>=0,则x可行。
按值依次建立主席树,每次只会增加一条修改为-1的链。(这里不用离散化)
维护前缀最大和后缀最大,还有和。
查询的时候就是前面一段的最大后缀+中间一段的和+后面一段的最大前缀。