BZOJ 2653: middle 主席树 二分

Posted 鲸头鹳

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了BZOJ 2653: middle 主席树 二分相关的知识,希望对你有一定的参考价值。

https://www.lydsy.com/JudgeOnline/problem.php?id=2653

因为是两个方向向外延伸所以显然不能对编号取前缀和,那么对排序取前缀和编号建线段树然后二分就可以了。

在一个值相对的线段树中,数小于该值的位置视为-1,数大于等于该值的位置视为+1. 那么当可取的区间内可以的到的最大的和>=0时该数就是合法的。

维护lmx, rmx, sum三个值分别表示该区间从左向右能得到的最大前缀和、从右向左能得到的最大前缀和、区间和。

二分到区间没有的数也没有关系,二分终究会收敛到区间中存在的数。

技术分享图片
 1 #include<iostream>
 2 #include<cstdio>
 3 #include<algorithm>
 4 #include<cstring>
 5 #include<cmath>
 6 using namespace std;
 7 #define LL long long
 8 #define pa pair<int,int>
 9 const int maxn=20010;
10 pa a[maxn];
11 int n,m;
12 int q[5]={};
13 int rt[maxn]={};
14 int siz[maxn*20]={},lmx[maxn*20]={},rmx[maxn*20]={},lc[maxn*20]={},rc[maxn*20]={},tot=0;
15 inline void updata(int x){
16     siz[x]=siz[lc[x]]+siz[rc[x]];
17     lmx[x]=max(lmx[lc[x]],siz[lc[x]]+lmx[rc[x]]);
18     rmx[x]=max(rmx[rc[x]],siz[rc[x]]+rmx[lc[x]]);
19 }
20 void build(int &x,int l,int r){
21     x=++tot; siz[x]=lmx[x]=rmx[x]=(r-l+1);
22     if(l==r)return;
23     int mid=(l+r)/2;
24     build(lc[x],l,mid);
25     build(rc[x],mid+1,r);
26 }
27 void inser(int &x,int y,int l,int r,int z){
28     x=++tot;siz[x]=siz[y];lmx[x]=lmx[y];rmx[x]=rmx[y];lc[x]=lc[y];rc[x]=rc[y];
29     if(l==r){siz[x]=-1;lmx[x]=0;rmx[x]=0;return;}
30     int mid=(l+r)/2;
31     if(z<=mid) inser(lc[x],lc[y],l,mid,z);
32     else inser(rc[x],rc[y],mid+1,r,z);
33     updata(x);
34 }
35 int getsum(int x,int l,int r,int zl,int zr){
36     if(zl>zr)return 0;
37     if(zl<=l&&r<=zr){return siz[x];}
38     int mid=(l+r)/2,ans=0;
39     if(zl<=mid)ans=getsum(lc[x],l,mid,zl,zr);
40     if(mid<zr)ans+=getsum(rc[x],mid+1,r,zl,zr);
41     return ans;
42 }
43 int getl(int x,int l,int r,int zl,int zr){
44     if(zl>zr)return 0;
45     if(zl<=l&&r<=zr)return lmx[x];
46     int mid=(l+r)/2,ans=0;
47     if(zl<=mid)ans=getl(lc[x],l,mid,zl,zr);
48     if(mid<zr)ans=max(ans,getl(rc[x],mid+1,r,zl,zr)+getsum(lc[x],l,mid,zl,mid));
49     return ans;
50 }
51 int getr(int x,int l,int r,int zl,int zr){
52     if(zl>zr)return 0;
53     if(zl<=l&&r<=zr)return rmx[x];
54     int mid=(l+r)/2,ans=0;
55     if(mid<zr)ans=getr(rc[x],mid+1,r,zl,zr);
56     if(zl<=mid)ans=max(ans,getr(lc[x],l,mid,zl,zr)+getsum(rc[x],mid+1,r,mid+1,zr));
57     return ans;
58 }
59 bool check(int x){
60     int z=getsum(rt[x],1,n,q[1],q[2]);//cout<<z<<endl;
61     int w=getl(rt[x],1,n,q[2]+1,q[3]);//cout<<w<<endl;
62     int k=getr(rt[x],1,n,q[0],q[1]-1);//cout<<k<<endl;
63     if(z+w+k>=0)return 1;
64     else return 0;
65 }
66 inline int find(){
67     int l=1,r=n,mid;
68     while(l<r){
69         mid=(l+r+1)/2;
70         if(check(mid))l=mid;
71         else r=mid-1;
72     }return l;
73 }
74 int main(){
75     scanf("%d",&n);
76     int x;
77     for(int i=1;i<=n;i++){ scanf("%d",&x); a[i]=make_pair(x,i); }
78     sort(a+1,a+1+n);build(rt[1],1,n);
79     for(int i=2;i<=n;i++){ rt[i]=rt[i-1];inser(rt[i],rt[i],1,n,a[i-1].second); }
80     int ans=0;scanf("%d",&m);
81     for(int i=1;i<=m;i++){
82         scanf("%d%d%d%d",&q[0],&q[1],&q[2],&q[3]);
83         q[0]=(q[0]+ans)%n+1;q[1]=(q[1]+ans)%n+1;
84         q[2]=(q[2]+ans)%n+1;q[3]=(q[3]+ans)%n+1;
85         sort(q,q+4);ans=a[find()].first;
86         printf("%d\n",ans);
87     }
88     return 0;
89 }
View Code

 

以上是关于BZOJ 2653: middle 主席树 二分的主要内容,如果未能解决你的问题,请参考以下文章

bzoj 2653 middle (主席树+二分)

BZOJ 2653 middle | 主席树

BZOJ 2653: middle [主席树 中位数]

bzoj2653: middle

bzoj2653: middle

BZOJ-2653middle 可持久化线段树 + 二分