[BZOJ4552][TJOI2016&&HEOI2016]排序(二分答案+线段树/线段树分裂与合并)
解法一:二分答案+线段树
首先我们知道,对于一个01序列排序,用线段树维护的话可以做到单次排序复杂度仅为log级别。
这道题只有一个询问,所以离线没有意义,而一个询问让我们很自然的想到二分答案。先二分出这个位置上的数是多少,然后将所有小于等于的数全部赋为0,其余赋为1,这样每次排序都是01序列排序了。如果最后p位置上的数为0则说明最终答案小于等于当前二分的答案,反之亦然。
这样这个问题就在$O(n \log^2 n)$的复杂度内解决了。
1 #include<cstdio> 2 #include<algorithm> 3 #define ls (x<<1) 4 #define rs ((x<<1)|1) 5 #define lson ls,L,mid 6 #define rson rs,mid+1,R 7 #define rep(i,l,r) for (int i=l; i<=r; i++) 8 using namespace std; 9 10 const int N=100100; 11 int n,m,qry,a[N],c[N],sm[N<<2],tag[N<<2]; 12 struct P{ int op,l,r; }b[N]; 13 14 void push(int x,int L,int R){ 15 if (tag[x]==-1) return; 16 int mid=(L+R)>>1; 17 sm[ls]=(mid-L+1)*tag[x]; tag[ls]=tag[x]; 18 sm[rs]=(R-mid)*tag[x]; tag[rs]=tag[x]; 19 tag[x]=-1; 20 } 21 22 void build(int x,int L,int R){ 23 tag[x]=-1; 24 if (L==R) { sm[x]=c[L]; return; } 25 int mid=(L+R)>>1; 26 build(lson); build(rson); 27 sm[x]=sm[ls]+sm[rs]; 28 } 29 30 void mdf(int x,int L,int R,int l,int r,int k){ 31 if (L==l && r==R){ sm[x]=k*(R-L+1); tag[x]=k; return; } 32 int mid=(L+R)>>1; push(x,L,R); 33 if (r<=mid) mdf(lson,l,r,k); 34 else if (l>mid) mdf(rson,l,r,k); 35 else mdf(lson,l,mid,k),mdf(rson,mid+1,r,k); 36 sm[x]=sm[ls]+sm[rs]; 37 } 38 39 int que(int x,int L,int R,int l,int r){ 40 if (L==l && r==R) return sm[x]; 41 int mid=(L+R)>>1; push(x,L,R); 42 if (r<=mid) return que(lson,l,r); 43 else if (l>mid) return que(rson,l,r); 44 else return que(lson,l,mid)+que(rson,mid+1,r); 45 } 46 47 int main(){ 48 scanf("%d%d",&n,&m); 49 rep(i,1,n) scanf("%d",&a[i]); 50 rep(i,1,m) scanf("%d%d%d",&b[i].op,&b[i].l,&b[i].r); 51 scanf("%d",&qry); 52 int L=1,R=n; 53 while (L<R){ 54 int mid=(L+R)>>1; 55 rep(i,1,n) if (a[i]<=mid) c[i]=0; else c[i]=1; 56 build(1,1,n); 57 rep(i,1,m){ 58 int l=b[i].l,r=b[i].r,s=que(1,1,n,l,r); 59 if (b[i].op==0){ 60 if (l<=r-s) mdf(1,1,n,l,r-s,0); 61 if (r-s+1<=r) mdf(1,1,n,r-s+1,r,1); 62 }else{ 63 if (l<=l+s-1) mdf(1,1,n,l,l+s-1,1); 64 if (l+s<=r) mdf(1,1,n,l+s,r,0); 65 } 66 } 67 if (que(1,1,n,qry,qry)==0) R=mid; else L=mid+1; 68 } 69 printf("%d\n",L); 70 return 0; 71 }
解法二:线段树分裂与合并
对每个连续的有序区间维护一棵线段树(初始时每个点都有一棵线段树)。
每次将一个区间排序时,先将跨过这个区间端点的区间分裂,再将这个区间包含的所有小区间合并。
set维护区间,区间合并通过线段树合并实现,时间复杂度为$O(n\log n)$。
可以垃圾回收,由于每次分裂只会新增log n个点,加上初始时新建的点,空间复杂度为$O((n+m)\log n)$。
这个方法不仅复杂度只有一个log,还能在最后求出整个序列而不是单个序列。
1 #include<set> 2 #include<cstdio> 3 #include<algorithm> 4 #define lson ls[x],L,mid 5 #define rson rs[x],mid+1,R 6 #define rep(i,l,r) for (int i=(l); i<=(r); i++) 7 using namespace std; 8 9 const int N=100010,M=7000010; 10 int n,m,x,nd,op,l,r,top,rt[N],rev[N],stk[M],ls[M],rs[M],sz[M]; 11 struct P{ int l,r; }; 12 bool operator <(const P &a,const P &b){ return a.r<b.r; } 13 set<P>S; 14 15 void del(int x){ ls[x]=rs[x]=sz[x]=0; stk[++top]=x; } 16 int get(){ return top ? stk[top--] : ++nd; } 17 18 void ins(int &x,int L,int R,int k){ 19 if (!x) x=get(); sz[x]++; 20 if (L==R) return; 21 int mid=(L+R)>>1; 22 if (k<=mid) ins(lson,k); else ins(rson,k); 23 } 24 25 void split(int &x,int &y,int k,int op){ 26 if (!x) return; 27 sz[y=get()]=sz[x]-k; sz[x]=k; 28 if (!op){ 29 if (sz[ls[x]]==k){ rs[y]=rs[x]; rs[x]=0; return; } 30 if (sz[ls[x]]>k){ rs[y]=rs[x]; rs[x]=0; split(ls[x],ls[y],k,op); } 31 else split(rs[x],rs[y],k-sz[ls[x]],op); 32 }else{ 33 if (sz[rs[x]]==k){ ls[y]=ls[x]; ls[x]=0; return; } 34 if (sz[rs[x]]>k){ ls[y]=ls[x]; ls[x]=0; split(rs[x],rs[y],k,op); } 35 else split(ls[x],ls[y],k-sz[rs[x]],op); 36 } 37 } 38 39 int merge(int x,int y){ 40 if (!x || !y) return x|y; 41 sz[x]+=sz[y]; 42 ls[x]=merge(ls[x],ls[y]); 43 rs[x]=merge(rs[x],rs[y]); 44 del(y); return x; 45 } 46 47 int que(int x,int L,int R,int k,int op){ 48 if (L==R) return L; 49 int mid=(L+R)>>1; 50 if (!op){ 51 if (sz[ls[x]]>=k) return que(lson,k,op); else return que(rson,k-sz[ls[x]],op); 52 }else{ 53 if (sz[rs[x]]>=k) return que(rson,k,op); else return que(lson,k-sz[rs[x]],op); 54 } 55 } 56 57 int main(){ 58 freopen("bzoj4552.in","r",stdin); 59 freopen("bzoj4552.out","w",stdout); 60 scanf("%d%d",&n,&m); 61 rep(i,1,n) scanf("%d",&x),ins(rt[i],1,n,x),S.insert((P){i,i}); 62 while (m--){ 63 scanf("%d%d%d",&op,&l,&r); 64 set<P>::iterator it=S.lower_bound((P){l,l}); 65 if (it->l<l){ 66 int L=it->l,R=it->r; 67 split(rt[L],rt[l],l-L,rev[L]); 68 S.erase(it); S.insert((P){L,l-1}); S.insert((P){l,R}); 69 rev[l]=rev[L]; 70 } 71 it=S.lower_bound((P){r+1,r+1}); 72 if (it!=S.end() && it->l<=r){ 73 int L=it->l,R=it->r; 74 split(rt[L],rt[r+1],r-L+1,rev[L]); 75 S.erase(it); S.insert((P){L,r}); S.insert((P){r+1,R}); 76 rev[r+1]=rev[L]; 77 } 78 it=S.lower_bound((P){l,l}); it++; 79 while (it!=S.end() && it->r<=r) rt[l]=merge(rt[l],rt[it->l]),it++; 80 it=S.lower_bound((P){l,l}); 81 while (it!=S.end() && it->r<=r) S.erase(it),it=S.lower_bound((P){l,l}); 82 S.insert((P){l,r}); rev[l]=op; 83 } 84 scanf("%d",&x); 85 set<P>::iterator it=S.lower_bound((P){x,x}); 86 printf("%d\n",que(rt[it->l],1,n,x-(it->l)+1,rev[it->l])); 87 return 0; 88 }