[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 }

 

posted @ 2018-03-10 10:25  HocRiser  阅读(231)  评论(0编辑  收藏  举报