(写篇博客证明自己还活着)
在OI中,有些时候我们会遇到一些维护多维信息的题目,比如经典的三维偏序,或者带修改区间k小值
这个时候有的dalao就会跳出来大喊“整体二分!”“CDQ!”
然而这并不是我们今天讨论的重点……并且在强制在线的情况下,上面这两种算法就无能为力了。
那么我们就需要用数据结构乱堆树套树的方法来解决这类问题。这类树套树解法以码量大和难调试著称。
通过用一种(棵?)数据结构维护一维信息,我们可以实现在线地维护多维信息。
那么让我们开始总结一下树套树吧!
一.线段树/树状数组套平衡树
这大概是没接触过树套树的同学接触的第一种树套树类型吧。。。
这种套法一般是用外层的树维护区间信息,在外层树的每一个节点放一棵内层树,内层的树维护权值信息。
比如说查区间第k大,用套平衡树的做法就是二分权值val->到这个区间对应的log个线段树节点上查val的rank值,加起来与目标值进行比较
这种做法的空间需求是$O(nlogn)$的,由于每个元素都会在logn个外层树节点中插入自己
而时间复杂度上,除了查询区间第k大,每次操作是O(nlog2n)的,
由于在logn个外层树上都要用$O(logn)$的时间查询
查询第k大是$O(nlog3n)$的,由于在logn个外层树上都要用$O(logn)$的时间查询。
这种类型的树套树,经典的有:
bzoj3196 二逼平衡树
1 #include<cstdio> 2 #include<cstdlib> 3 #include<cstring> 4 #include<ctime> 5 #include<cmath> 6 using namespace std; 7 const int N=50000+10; 8 int val[N],n,m,a,b,c,o; 9 struct node 10 { 11 node* ch[2]; 12 int rank,val,size,ge; 13 node (int x){val=x;rank=rand();size=ge=1;ch[1]=ch[0]=NULL;} 14 void tain() 15 { 16 size=1; 17 if(ch[0])size+=ch[0]->size; 18 if(ch[1])size+=ch[1]->size; 19 } 20 }; 21 inline int s(node* o){return o?o->size:0;} 22 node* root[4*N]; 23 void rotate(node* &o,int d) 24 { 25 node* k=o->ch[d^1];o->ch[d^1]=k->ch[d];k->ch[d]=o; 26 o->tain();k->tain();o=k; 27 } 28 void insert(node* &o,int val) 29 { 30 if(o==NULL){o=new node(val);return;} 31 if(val<o->val) 32 { 33 insert(o->ch[0],val); 34 if(o->ch[0]->rank > o->rank) 35 rotate(o,1); 36 } 37 else 38 { 39 insert(o->ch[1],val); 40 if(o->ch[1]->rank > o->rank) 41 rotate(o,0); 42 } 43 o->tain(); 44 } 45 inline void build(int le,int ri,int num) 46 { 47 for(int i=le;i<=ri;i++)insert(root[num],val[i]); 48 } 49 void treeins(int le,int ri,int num) 50 { 51 build(le,ri,num); 52 if(le==ri)return; 53 int mi=(le+ri)>>1; 54 treeins(le,mi,num<<1); 55 treeins(mi+1,ri,(num<<1)|1); 56 } 57 void remove(node* &o,int val) 58 { 59 if(val==o->val) 60 { 61 if(o->ch[0]&&o->ch[1]) 62 { 63 int d2=(o->ch[0]->rank > o->ch[1]->rank)?1:0; 64 rotate(o,d2);remove(o->ch[d2],val); 65 } 66 else 67 { 68 node* u=NULL; 69 if(o->ch[0]!=NULL)u=o->ch[0]; 70 else u=o->ch[1]; 71 delete o; 72 o=u; 73 } 74 } 75 else 76 if(val< o->val)remove(o->ch[0],val); 77 else remove(o->ch[1],val); 78 if(o)o->tain(); 79 } 80 inline int find_rank(node* o,int val) 81 { 82 int ge=0; 83 while(o) 84 { 85 if(val> o->val)ge+=s(o->ch[0])+1,o=o->ch[1]; 86 else o=o->ch[0]; 87 } 88 return ge; 89 } 90 int tree_rank(int le,int ri,int num,int val) 91 { 92 if(a<=le&&ri<=b)return find_rank(root[num],val); 93 int mi=(le+ri)>>1; 94 int ret=0; 95 if(b<=mi)return tree_rank(le,mi,num<<1,val); 96 if(mi<a)return tree_rank(mi+1,ri,(num<<1)|1,val); 97 return tree_rank(le,mi,num<<1,val)+tree_rank(mi+1,ri,(num<<1)|1,val); 98 } 99 inline int divide_rank(int val) 100 { 101 int l=0,r=100000000; 102 while(l<=r) 103 { 104 int mi=(l+r)>>1; 105 int ans=tree_rank(1,n,1,mi)+1; 106 if(ans<=val)l=mi+1; 107 else r=mi-1; 108 } 109 return r; 110 } 111 inline int pre(int le,int ri,int val) 112 { 113 int tmp=tree_rank(1,n,1,val); 114 return divide_rank(tmp); 115 } 116 inline int re(int le,int ri,int val) 117 { 118 int tmp=tree_rank(1,n,1,val+1)+1; 119 return divide_rank(tmp); 120 } 121 void change(int le,int ri,int num,int pos,int pre,int now) 122 { 123 remove(root[num],pre); 124 insert(root[num],now); 125 if(le==ri)return; 126 int mi=(le+ri)>>1; 127 if(pos<=mi)change(le,mi,num<<1,pos,pre,now); 128 else change(mi+1,ri,(num<<1)|1,pos,pre,now); 129 } 130 int main() 131 { 132 scanf("%d%d",&n,&m); 133 for(int i=1;i<=n;i++) 134 scanf("%d",&val[i]); 135 treeins(1,n,1); 136 while(m--) 137 { 138 scanf("%d",&o); 139 switch(o) 140 { 141 case 1: 142 scanf("%d%d%d",&a,&b,&c); 143 printf("%d\n",tree_rank(1,n,1,c)+1);break; 144 case 2: 145 scanf("%d%d%d",&a,&b,&c); 146 printf("%d\n",divide_rank(c));break; 147 case 3: 148 scanf("%d%d",&a,&b); 149 change(1,n,1,a,val[a],b);val[a]=b;break; 150 case 4: 151 scanf("%d%d%d",&a,&b,&c); 152 printf("%d\n",pre(a,b,c));break; 153 case 5: 154 scanf("%d%d%d",&a,&b,&c); 155 printf("%d\n",re(a,b,c));break; 156 } 157 } 158 //while(1); 159 }