FHQ Treap及其可持久化与朝鲜树式重构

FHQ Treap,又称无旋treap,一种不基于旋转机制的平衡树,可支持所有有旋treap、splay等能支持的操作(只有在LCT中会比splay复杂度多一个log)。最重要的是,它是OI中唯一一种支持可持久化的平衡树。

以下只提供题表与代码,不提供教程。

 

1.[BZOJ3224]普通平衡树

FHQ Treap的应用一:基础平衡树操作模板题。

由于merge、split和树高是$O(\log n)$的,所以所有基础操作都是$O(\log n)$的。

 1 #include<cstdio>
 2 #include<algorithm>
 3 #define P pair<int,int>
 4 #define rep(i,l,r) for (int i=l; i<=r; i++)
 5 using namespace std;
 6 
 7 const int N=200100,inf=1000000000;
 8 int T,nd,rt,op,ans,x,ls[N],rs[N],h[N],sz[N],v[N];
 9 
10 void upd(int x){ sz[x]=sz[ls[x]]+sz[rs[x]]+1; }
11 int get(int x){ v[++nd]=x; sz[nd]=1; h[nd]=rand(); return nd; }
12 
13 int merge(int x,int y){
14     if (!x || !y) return x+y;
15     if (h[x]<h[y]) { rs[x]=merge(rs[x],y); upd(x); return x; }
16                 else { ls[y]=merge(x,ls[y]); upd(y); return y; }
17 }
18 
19 P split(int x,int k){
20     if (!x) return P(0,0);
21     P tmp;
22     if (k<=sz[ls[x]]) tmp=split(ls[x],k),ls[x]=tmp.second,upd(x),tmp=P(tmp.first,x);
23                      else tmp=split(rs[x],k-sz[ls[x]]-1),rs[x]=tmp.first,upd(x),tmp=P(x,tmp.second);
24     return tmp;
25 }
26 
27 int rank(int x,int k){
28     if (!x) return 0;
29     if (v[x]>=k) return rank(ls[x],k); else return rank(rs[x],k)+sz[ls[x]]+1;
30 }
31 
32 int find(int x,int k){
33     if (!x) return 0;
34     if (k==sz[ls[x]]+1) return x;
35     if (k<=sz[ls[x]]) return find(ls[x],k); else return find(rs[x],k-sz[ls[x]]-1);
36 }
37 
38 void ins(int k){
39     int v=rank(rt,k); P x=split(rt,v);
40     rt=merge(merge(x.first,get(k)),x.second);
41 }
42 
43 void del(int k){
44     int v=rank(rt,k);
45     P x=split(rt,v),y=split(x.second,1);
46     rt=merge(x.first,y.second);
47 }
48 
49 int main(){
50     for (scanf("%d",&T); T--; ){
51         scanf("%d%d",&op,&x);
52         if (op==1) ins(x);
53         if (op==2) del(x);
54         if (op==3) printf("%d\n",rank(rt,x)+1);
55         if (op==4) printf("%d\n",v[find(rt,x)]);
56         if (op==5) printf("%d\n",v[find(rt,rank(rt,x))]);
57         if (op==6) printf("%d\n",v[find(rt,rank(rt,x+1)+1)]);
58     }
59     return 0;
60 }
BZOJ3224

 

2.[CF702F]T-shirts

FHQ Treap的应用二:树的分裂与合并、树上延迟标记。

n种物品(ci,qi)k个人(bi),每个人将所有物品按qi从大到小,相同q按ci从小到大依次考虑,若剩余钱数bi>=ci则买一件否则跳过,问每个人买了几件物品。

由于每个人考虑物品的顺序是一样的,考虑从物品分配给人的角度处理。将物品按题目要求排序后依次处理,每次将所有钱数足够的人钱数集体减c。

对人按剩余钱数建Treap,当考虑物品i时,将树按钱数是否足够分裂成两边,并在足够的一边打上集体-c的标记。此时树中节点已无序,于是每次暴力将右边的最小值插入到左边直到右边最小值大于左边最大值为止,最后再将两边合并起来。

考虑复杂度,当一个人x被从右边转到左边时,有x-c<c,又由于c<=x,故x-c<x/2,即每次至少减半,于是总复杂度均摊$O(n\log^2 n)$

 1 #include<cstdio>
 2 #include<algorithm>
 3 #define rep(i,l,r) for (int i=(l); i<=(r); i++)
 4 typedef long long ll;
 5 using namespace std;
 6 
 7 const int N=200010;
 8 int m,n,rt,nd,res,id[N],b[N],ans[N];
 9 struct Q{ int q,c; }s[N];
10 struct Tr{ int ls,rs,sz,k,tag2,hr,id; ll v,tag1; }v[N];
11 struct P{ int x,y; };
12 bool operator <(const Q &a,const Q &b){ return a.q!=b.q ? a.q>b.q : a.c<b.c; }
13 bool cmp(int x,int y){ return b[x]<b[y]; }
14 
15 void upd(int x){ v[x].sz=v[v[x].ls].sz+v[v[x].rs].sz+1; }
16 void put(int x,ll w,int k){ v[x].v-=w; v[x].k+=k; v[x].tag1+=w; v[x].tag2+=k; }
17 
18 void push(int x){
19     if (v[x].tag1 || v[x].tag2){
20         put(v[x].ls,v[x].tag1,v[x].tag2);
21         put(v[x].rs,v[x].tag1,v[x].tag2);
22         v[x].tag1=v[x].tag2=0;
23     }
24 }
25 
26 int merge(int x,int y){
27     if (!x || !y) return x|y;
28     if (v[x].hr<v[y].hr){ push(x); v[x].rs=merge(v[x].rs,y); upd(x); return x; }
29         else{ push(y); v[y].ls=merge(x,v[y].ls); upd(y); return y; }
30 }
31 
32 P split(int x,int k){
33     if (!x) return (P){0,0};
34     push(x);
35     if (k<v[x].v){ P t=split(v[x].ls,k); v[x].ls=t.y; upd(x); return (P){t.x,x}; }
36         else{ P t=split(v[x].rs,k); v[x].rs=t.x; upd(x); return (P){x,t.y}; }
37 }
38 
39 int ins(int x,int k){ P t=split(x,v[k].v); return merge(merge(t.x,k),t.y); }
40 
41 int del(int x){
42     if (!v[x].ls){ int t=v[x].rs; v[x].rs=0; return t; }
43     v[x].ls=del(v[x].ls); upd(x); return x;
44 }
45 
46 int Mn(int x){ push(x); return v[x].ls ? Mn(v[x].ls) : x; }
47 int Mx(int x){ push(x); return v[x].rs ? Mx(v[x].rs) : x; }
48 
49 void dfs(int x){
50     push(x);
51     if (v[x].ls) dfs(v[x].ls);
52     ans[v[x].id]=v[x].k;
53     if (v[x].rs) dfs(v[x].rs);
54 }
55 
56 int main(){
57     scanf("%d",&m);
58     rep(i,1,m) scanf("%d%d",&s[i].c,&s[i].q);
59     sort(s+1,s+m+1); scanf("%d",&n);
60     rep(i,1,n) scanf("%d",&b[i]),id[i]=i;
61     sort(id+1,id+n+1,cmp);
62     rep(i,1,n) v[++nd]=(Tr){0,0,1,0,0,rand(),id[i],b[id[i]],0},rt=merge(rt,nd);
63     rep(i,1,m){
64         P t=split(rt,s[i].c-1); put(t.y,s[i].c,1);
65         while (1){
66             int x=Mn(t.y); if (!x) break;
67             int y=Mx(t.x); if (!y) break;
68             if (v[x].v>=v[y].v) break;
69             t.y=del(t.y); t.x=ins(t.x,x);
70         }
71         rt=merge(t.x,t.y);
72     }
73     dfs(rt);
74     rep(i,1,n) printf("%d ",ans[i]);
75     return 0;
76 }
CF702F

 

3.[Luogu3835][模板]可持久化平衡树 && [Luogu5055][模板]可持久化文艺平衡树

FHQ Treap的应用三:可持久化与可持久化延迟标记。

与主席树相对于普通线段树的关系类似,可持久化时,在所有需要修改时新建节点即可。(注意下传标记的时候的两个儿子因为被更改也需要新建)

注意push()和upd(),以及空间复杂度$O(n\log n)$

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<iostream>
 4 #include<algorithm>
 5 #define rep(i,l,r) for (int i=(l); i<=(r); i++)
 6 typedef long long ll;
 7 using namespace std;
 8 
 9 const int N=500010,inf=2147483647;
10 int n,w,op,x,nd,ans,rt[N];
11 struct Tr{ int ls,rs,hr,v,sz; }v[N*50];
12 struct P{ int x,y; };
13 
14 void upd(int x){ v[x].sz=v[v[x].ls].sz+v[v[x].rs].sz+1; }
15 int get(int x){ v[++nd].sz=1; v[nd].v=x; v[nd].hr=rand(); return nd; }
16 
17 int merge(int x,int y){
18     if (!x || !y) return x|y;
19     int p=++nd;
20     if (v[x].hr<v[y].hr){ v[p]=v[x]; v[p].rs=merge(v[p].rs,y); upd(p); return p; }
21         else { v[p]=v[y]; v[p].ls=merge(x,v[p].ls); upd(p); return p; }
22 }
23 
24 P split(int x,int k){
25     if (!x) return (P){0,0};
26     P t; int p=++nd; v[p]=v[x];
27     if (k<v[p].v){ t=split(v[p].ls,k); v[p].ls=t.y; upd(p); return (P){t.x,p}; }
28         else { t=split(v[p].rs,k); v[p].rs=t.x; upd(p); return (P){p,t.y}; }
29 }
30 
31 int ins(int x,int k){ P t=split(x,k); return merge(merge(t.x,get(k)),t.y); }
32 
33 int del(int x,int k){
34     P t1=split(x,k),t2=split(t1.x,k-1);
35     t2.y=merge(v[t2.y].ls,v[t2.y].rs);
36     return merge(merge(t2.x,t2.y),t1.y);
37 }
38 
39 int rnk(int x,int k){
40     if (!x) return 0;
41     if (k<=v[x].v) return rnk(v[x].ls,k); else return v[v[x].ls].sz+1+rnk(v[x].rs,k);
42 }
43 
44 int que(int x,int k){
45     if (k==v[v[x].ls].sz+1) return v[x].v;
46     if (k<=v[v[x].ls].sz) return que(v[x].ls,k);
47         else return que(v[x].rs,k-v[v[x].ls].sz-1);
48 }
49 
50 void pre(int x,int k){
51     if (!x) return;
52     if (k>v[x].v) ans=max(ans,v[x].v),pre(v[x].rs,k); else pre(v[x].ls,k);
53 }
54 
55 void nxt(int x,int k){
56     if (!x) return;
57     if (k<v[x].v) ans=min(ans,v[x].v),nxt(v[x].ls,k); else nxt(v[x].rs,k);
58 }
59 
60 int main(){
61     freopen("P3835.in","r",stdin);
62     freopen("P3835.out","w",stdout);
63     scanf("%d",&n);
64     rep(i,1,n){
65         scanf("%d%d%d",&w,&op,&x); rt[i]=rt[w];
66         if (op==1) rt[i]=ins(rt[w],x);
67         if (op==2) rt[i]=del(rt[w],x);
68         if (op==3) printf("%d\n",rnk(rt[w],x)+1);
69         if (op==4) printf("%d\n",que(rt[w],x));
70         if (op==5) ans=-inf,pre(rt[w],x),printf("%d\n",ans);
71         if (op==6) ans=inf,nxt(rt[w],x),printf("%d\n",ans);
72     }
73     return 0;
74 }
Luogu3835
 1 #include<cstdio>
 2 #include<cstring>
 3 #include<iostream>
 4 #include<algorithm>
 5 #define rep(i,l,r) for (int i=(l); i<=(r); i++)
 6 typedef long long ll;
 7 using namespace std;
 8 
 9 const int N=200010;
10 ll ans;
11 int n,w,op,l,r,k,x,nd,rt[N];
12 struct Tr{ int ls,rs,v,sz,tag,hr; ll sm; }v[N*80];
13 struct P{ int x,y; };
14 
15 void upd(int x){
16     v[x].sz=v[v[x].ls].sz+v[v[x].rs].sz+1;
17     v[x].sm=v[v[x].ls].sm+v[v[x].rs].sm+v[x].v;
18 }
19 
20 int get(int x){ nd++; v[nd].v=v[nd].sm=x; v[nd].sz=1; v[nd].hr=rand(); return nd; }
21 
22 void put(int &x){ if (x) v[++nd]=v[x],x=nd,swap(v[x].ls,v[x].rs),v[x].tag^=1; }
23 void push(int x){ if (v[x].tag) put(v[x].ls),put(v[x].rs),v[x].tag=0; }
24 
25 int merge(int x,int y){
26     if (!x || !y) return x|y;
27     int p=++nd;
28     if (v[x].hr<v[y].hr){ v[p]=v[x]; push(p); v[p].rs=merge(v[p].rs,y); upd(p); return p; }
29         else{ v[p]=v[y]; push(p); v[p].ls=merge(x,v[p].ls); upd(p); return p; }
30 }
31 
32 P split(int x,int k){
33     if (!x) return (P){0,0};
34     P t; int p=++nd; v[p]=v[x]; push(p);
35     if (k<=v[v[x].ls].sz){ t=split(v[p].ls,k); v[p].ls=t.y; upd(p); return (P){t.x,p}; }
36         else{ t=split(v[p].rs,k-v[v[x].ls].sz-1); v[p].rs=t.x; upd(p); return (P){p,t.y}; }
37 }
38 
39 int ins(int x,int pos,int k){ P t=split(x,pos); return merge(merge(t.x,get(k)),t.y); }
40 
41 int del(int x,int k){ P t1=split(x,k),t2=split(t1.x,k-1); return merge(t2.x,t1.y); }
42 
43 int rev(int x,int l,int r){
44     P t1=split(x,l-1),t2=split(t1.y,r-l+1);
45     put(t2.x); return merge(t1.x,merge(t2.x,t2.y));
46 }
47 
48 ll que(int x,int l,int r){ P t1=split(x,l-1),t2=split(t1.y,r-l+1); return v[t2.x].sm; }
49 
50 int main(){
51     freopen("P5055.in","r",stdin);
52     freopen("P5055.out","w",stdout);
53     scanf("%d",&n);
54     rep(i,1,n){
55         scanf("%d%d",&w,&op);
56         if (op==1) scanf("%d%d",&x,&k),x^=ans,k^=ans,rt[i]=ins(rt[w],x,k);
57         if (op==2) scanf("%d",&x),x^=ans,rt[i]=del(rt[w],x);
58         if (op==3) scanf("%d%d",&l,&r),l^=ans,r^=ans,rt[i]=rev(rt[w],l,r);
59         if (op==4) scanf("%d%d",&l,&r),l^=ans,r^=ans,rt[i]=rt[w],printf("%lld\n",ans=que(rt[w],l,r));
60     }
61     return 0;
62 }
Luogu5055

 

4.[CF1056G]Take Matro

FHQ Treap的应用四:区间复制与朝鲜树式重构.

n个地铁站围成环,若当前位于前m个地铁站则顺时针坐t站否则逆时针坐t站。每坐一次t--。

给定n,m,初始位于的地铁站与t的初值,问最终位于的地铁站。(n,m<=1e5,t<=1e12)

由于每次只与t%n有关,所以首先暴力走成t是n的倍数,然后预处理出从每个站开始走n次(即t=n,t=n-1,...,t=1各走一次)后到的地点,最后倍增到t/n即为答案。

问题就在于如何求出每个点走n次到的位置,对n个地铁站建Treap然后发现每次相当于一个区间平移操作,使用FHQ Treap的区间复制操作即可。

一下内容均为口胡,来自一位神仙提出的方法:https://www.cnblogs.com/Gloid/p/10388719.html

具体来说就是:通过可持久化split复制得到一个区间,重新按顺序拼成一个新的序列就是新的答案了。实现过程主要有几个问题:

1.根据FHQ Treap的时间复杂度证明发现,当将一个区间复制到另一颗树中某位置进行merge的时候,由于两棵树之间的随机权值是互不相关的,所以概率分析实际上是错误的,所以复杂度是没有保证的。

为了解决这一问题,考虑每次合并需要的正确概率,x中最小元素比y中最小元素小的概率(即将y合并到x树上的概率)为$\frac{size[x]}{size[x]+size[y]}$,于是我们合并的时候直接采用随机函数,以这个概率合并即可。(即rand()%(size[x]+size[y])<size[x]))

2.即便考虑了情况1,FHQ Treap也无法保证树高,于是考虑定期重构。

一种方法是每进行一定次数操作就暴力将整棵树拍扁重构,实测效果最好的位于每5000次操作重构一次附近。

一种方法是考虑替罪羊树,当子树重量失衡时重构子树,但发现在可持久化树中,仅重构一棵子树是比较复杂的事情(也可能是我水平低没有想到什么好方法),于是考虑使用朝鲜树,即对每个点维护在树中的高度,当整棵树的高度超过预设上限时重构整棵树。实测高度上限在100左右最优。

当然,由于有拍扁重构的过程,需要使用(1)中的随机合并方法,因为在建树时如果暴力merge复杂度是$O(n\log n)$的,普通建树是线性的,而普通建树时是不给每个节点随机权值并调整的。

 1 #include<cstdio>
 2 #include<algorithm>
 3 #include<iostream>
 4 #define rep(i,l,r) for (int i=(l); i<=(r); i++)
 5 typedef long long ll;
 6 using namespace std;
 7 
 8 const int N=100010;
 9 ll t;
10 int n,m,s,nd,rt,tot,to[N][50];
11 struct Tr{ int ls,rs,v,sz,d; }v[N<<8];
12 struct P{ int x,y; };
13 
14 int jump(ll x,ll y){
15     if (x<=m) x+=y; else x-=y;
16     return (x%n+n-1)%n+1;
17 }
18 
19 void upd(int x){ v[x].sz=v[v[x].ls].sz+v[v[x].rs].sz+1; v[x].d=max(v[v[x].ls].d,v[v[x].rs].d)+1; }
20 int get(int k){ v[++nd]=(Tr){0,0,k,1,1}; return nd; }
21 void dfs(int x){ if (v[x].ls) dfs(v[x].ls); to[++tot][0]=v[x].v; if (v[x].rs) dfs(v[x].rs); }
22 void Print(int x){ if (v[x].ls) Print(v[x].ls); printf("%d ",v[x].v); if (v[x].rs) Print(v[x].rs); }
23 
24 int merge(int x,int y){
25     if (!x || !y){ v[++nd]=v[x|y]; return nd; }
26     int p=++nd;
27     if (1ll*rand()*(v[x].sz+v[y].sz)<1ll*v[x].sz*RAND_MAX)
28         { v[p]=v[x]; v[p].rs=merge(v[p].rs,y); upd(p); return p; }
29     else{ v[p]=v[y]; v[p].ls=merge(x,v[p].ls); upd(p); return p; }
30 }
31 
32 P split(int x,int k){
33     if (!x) return (P){0,0};
34     P t; int p=++nd; v[p]=v[x];
35     if (k<=v[v[x].ls].sz){ t=split(v[p].ls,k); v[p].ls=t.y; upd(p); return (P){t.x,p}; }
36         else{ t=split(v[p].rs,k-v[v[x].ls].sz-1); v[p].rs=t.x; upd(p); return (P){p,t.y}; }
37 }
38 
39 int build(int L,int R){
40     if (L>R) return 0;
41     int mid=(L+R)>>1,x=get(to[mid][0]);
42     v[x].ls=build(L,mid-1); v[x].rs=build(mid+1,R);
43     upd(x); return x;
44 }
45 
46 int main(){
47     freopen("1056G.in","r",stdin);
48     freopen("1056G.out","w",stdout);
49     scanf("%d%d%d",&n,&m,&s); cin>>t;
50     for (; t%n; t--) s=jump(s,t); t/=n;
51     rep(i,1,n) to[i][0]=i; rt=build(1,n);
52     rep(i,1,n-1){
53         int lst=rt;
54         if (m+i<=n){ P a=split(lst,m+i),b=split(a.x,i); rt=b.y; }
55             else{ P a=split(lst,i),b=split(a.x,m+i-n); rt=merge(a.y,b.x); }
56         if (m+1-i>0){ P a=split(lst,n-i),b=split(a.x,m-i); rt=merge(rt,b.y); }
57             else{ P a=split(lst,m+n-i),b=split(a.x,n-i); rt=merge(rt,merge(a.y,b.x)); }
58         if (v[rt].d>100) tot=0,dfs(rt),nd=0,rt=build(1,n);
59     }
60     tot=0; dfs(rt);
61     rep(j,1,49) rep(i,1,n) to[i][j]=to[to[i][j-1]][j-1];
62     for (int j=49; ~j; j--) if (t&(1ll<<j)) s=to[s][j];
63     printf("%d\n",s);
64     return 0;
65 }
CF1056G

 

 

5.[HDU6087]Rikka with Sequence

FHQ Treap的应用五:快速幂区间循环复制。

当然这只是一个小应用,题意要求支持三种操作:

1.求A[l,r]的和。 在Treap中维护子树和即可。

3.将A[l,r]还原为最初始时的值。这个使用(4)中的区间复制即可。

2.for (int i=l; i<=r; i++) A[i]=A[i-k]。

发现这实际上是将[l-k,l-1]循环复制到[l,r]上。为了降低复杂度,使用类似快速幂的思想。每次自我复制一份,最后再用split()将剩余部分除去。

这题最毒瘤的地方在于它给$O(n\log^2 n)$空间复杂度的程序只开64M空间(可能是有比我复杂度优的做法),于是要注意定期重构。我的代码由于空间问题无法使用朝鲜树式重构,于是选择每1000次操作重构一次。当然还有一种方法是用替罪羊的判定方法,当某子树失衡时暴力重构整棵树,但我没有实现这种方法。

由于2操作,时空复杂度都是$O(n\log^2 n)$的。

 1 #include<cstdio>
 2 #include<algorithm>
 3 #define rep(i,l,r) for (int i=(l); i<=(r); i++)
 4 typedef long long ll;
 5 using namespace std;
 6 
 7 const int N=200010;
 8 int n,m,tmp,nd,k,op,lst,tot,l,r,rt,a[N];
 9 struct Tr{ int ls,rs,v,sz; ll sm; }v[N*8];
10 struct P{ int x,y; };
11 
12 int get(int k){ v[++nd]=(Tr){0,0,k,1,k}; return nd; }
13 
14 void upd(int x){
15     v[x].sz=v[v[x].ls].sz+v[v[x].rs].sz+1;
16     v[x].sm=v[v[x].ls].sm+v[v[x].rs].sm+v[x].v;
17 }
18 
19 int merge(int x,int y){
20     if (!x || !y){ v[++nd]=v[x|y]; return nd; }
21     int p=++nd;
22     if (rand()%(v[x].sz+v[y].sz)<=v[x].sz)
23         v[p]=v[x],v[p].rs=merge(v[p].rs,y); else v[p]=v[y],v[p].ls=merge(x,v[p].ls);
24     upd(p); return p;
25 }
26 
27 P split(int x,int k){
28     if (!x) return (P){0,0};
29     int p=++nd; v[p]=v[x];
30     if (k<=v[v[x].ls].sz){ P t=split(v[p].ls,k); v[p].ls=t.y; upd(p); return (P){t.x,p}; }
31         else{ P t=split(v[p].rs,k-v[v[x].ls].sz-1); v[p].rs=t.x; upd(p); return (P){p,t.y}; }
32 }
33 
34 int copy(int x,int l,int r){ P t=split(x,r); return split(t.x,l-1).y; }
35 
36 int ksm(int a,int b){
37     int res=0;
38     for (; b; a=merge(a,a),b>>=1)
39         res=merge(res,a);
40     return res;
41 }
42 
43 void dfs(int x){
44     if (v[x].ls) dfs(v[x].ls);
45     a[++tot]=v[x].v;
46     if (v[x].rs) dfs(v[x].rs);
47 }
48 
49 int build(int L,int R){
50     if (L>R) return 0;
51     int mid=(L+R)>>1,x=get(a[mid]);
52     v[x].ls=build(L,mid-1); v[x].rs=build(mid+1,R);
53     upd(x); return x;
54 }
55 
56 int main(){
57     freopen("hdu6087.in","r",stdin);
58     freopen("hdu6087.out","w",stdout);
59     scanf("%d%d",&n,&m); int lim=1000;
60     rep(i,1,n) scanf("%d",&a[i]);
61     rt=build(1,n); tmp=nd; lst=rt;
62     while (m--){
63         scanf("%d%d%d",&op,&l,&r);
64         if (op==1) printf("%lld\n",v[copy(rt,l,r)].sm);
65         if (op==2){
66             scanf("%d",&k);
67             int x=split(ksm(copy(rt,l-k,l-1),(r-l+1)/k+1),r-l+1).x;
68             P t1=split(rt,l-1),t2=split(t1.y,r-l+1);
69             rt=merge(t1.x,merge(x,t2.y));
70         }
71         if (op==3){
72             P t1=split(rt,l-1),t2=split(t1.y,r-l+1);
73             rt=merge(t1.x,merge(copy(lst,l,r),t2.y));
74         }
75         if (!(m%lim)) tot=0,dfs(rt),nd=tmp,rt=build(1,tot);
76     }
77     return 0;
78 }
HDU6087

 

posted @ 2019-02-24 16:08  HocRiser  阅读(790)  评论(0编辑  收藏  举报