说白了,就是在一个树形数据结构上,每个点不再是一个节点,而是另外一个树形数据结构。
空间时间复杂度大多数都是O(nlogn)
线段树套平衡树
许多树套树都可以用线段树套平衡树解决。
空间O(nlogn)是很可观的。
各种区间找值的问题,可以游刃有余解决。
(虽然常数比较大)
例如模板:
下标线段树里套权值平衡树
线段树节点O(4*N)
平衡树节点O(NlogN)
对于第k大,外面二分一下,然后查比mid小的数的个数,如果<k那么可以。O(log^3n)可过。
代码:
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
// luogu-judger-enable-o2 #include<bits/stdc++.h> #define reg register int #define il inline #define numb (ch^'0') #define mid ((l+r)>>1) using namespace std; typedef long long ll; il void rd(int &x){ char ch;x=0;bool fl=false; while(!isdigit(ch=getchar()))(ch=='-')&&(fl=true); for(x=numb;isdigit(ch=getchar());x=x*10+numb); (fl==true)&&(x=-x); } namespace Miracle{ const int N=5*1e4+5; const int inf=2147483647; int rt[4*N]; struct node{ int ch[2]; int fa; int sum,sz; int val; }t[N*20]; int cur;//id int dp[233],dc; int n,m; int a[N]; int nc(int v,int f){ int r=dc?dp[dc--]:++cur; t[r].val=v;t[r].fa=f;t[r].sz=t[r].sum=1; t[r].ch[0]=t[r].ch[1]=0; return r; } void dele(int o){ dp[++dc]=o; } void pushup(int x){ if(x==0) return; t[x].sz=t[t[x].ch[0]].sz+t[t[x].ch[1]].sz+t[x].sum; } void rotate(int x){ int y=t[x].fa,d=t[y].ch[1]==x; t[t[y].ch[d]=t[x].ch[!d]].fa=y; t[t[x].fa=t[y].fa].ch[t[t[y].fa].ch[1]==y]=x; t[t[x].ch[!d]=y].fa=x; pushup(y); } void splay(int o,int x,int f){ while(t[x].fa!=f){ int y=t[x].fa,z=t[y].fa; if(z!=f){ rotate((t[y].ch[0]==x)==(t[z].ch[0]==y)?y:x); } rotate(x); } if(f==0) rt[o]=x; pushup(x); } void ins(int o,int v){//warning!! maybe no rt if(!rt[o]){ rt[o]=nc(v,0); return; } int x=rt[o]; while(1){ t[x].sz++; if(t[x].val==v){ t[x].sum++;break; } int d=t[x].val<v; if(!t[x].ch[d]){ //cout<<" nc "<<endl; t[x].ch[d]=nc(v,x); x=t[x].ch[d]; break; } x=t[x].ch[d]; } splay(o,x,0); } void build(int o,int l,int r){ //cout<<o<<" : "<<l<<" "<<r<<" "<<rt[o]<<endl; for(reg i=l;i<=r;++i){ // cout<<"ins "<<i<<" "<<a[i]<<endl; ins(o,a[i]); } } void remove(int o,int v){//warning!! maybe no rt int x=rt[o]; while(t[x].val!=v){ int d=t[x].val<v; x=t[x].ch[d]; } splay(o,x,0); t[x].sum--; t[x].sz--; if(t[x].sum) return; dele(x); int son=t[x].ch[1]; if(!son){ rt[o]=t[x].ch[0]; t[rt[o]].fa=0; return; } else{ while(t[son].ch[0]) son=t[son].ch[0]; splay(o,son,x); t[t[son].ch[0]=t[x].ch[0]].fa=son; t[son].fa=0; pushup(son); rt[o]=son; } } int pre(int o,int c){ int x=rt[o]; int ret=-inf; while(x){ if(t[x].val<c) ret=max(ret,t[x].val); if(t[x].val<c){ x=t[x].ch[1]; } else x=t[x].ch[0]; } return ret; } int bac(int o,int c){ int x=rt[o]; int ret=inf; //cout<<" oooooo "<<o<<endl; while(x){ if(t[x].val>c) ret=min(ret,t[x].val); if(t[x].val>c){ x=t[x].ch[0]; } else x=t[x].ch[1]; } //cout<<" ret "<<ret<<endl; return ret; } int rk(int o,int c){//±ÈcСµÄÊýµÄ¸öÊý int x=rt[o]; int ret=0; int cnt=0; while(x){ ++cnt; //if(cnt<=15) cout<<"xx "<<x<<endl; if(t[x].val<c){ ret+=t[x].sum+t[t[x].ch[0]].sz; x=t[x].ch[1]; } else if(t[x].val==c){ ret+=t[t[x].ch[0]].sz;return ret; } else if(t[x].val>c){ x=t[x].ch[0]; } } return ret; } void jian(int x,int l,int r){ if(l==r){ build(x,l,r);return; } build(x,l,r); jian(x<<1,l,mid); jian(x<<1|1,mid+1,r); } int pai(int x,int l,int r,int L,int R,int c){ if(L<=l&r<=R){ return rk(x,c); } int ret=0; if(L<=mid) ret+=pai(x<<1,l,mid,L,R,c); if(mid<R) ret+=pai(x<<1|1,mid+1,r,L,R,c); return ret; } void chan(int x,int l,int r,int to,int b,int c){ if(l==r){ remove(x,b);ins(x,c); return; } remove(x,b);ins(x,c); if(to<=mid) chan(x<<1,l,mid,to,b,c); else chan(x<<1|1,mid+1,r,to,b,c); } int qian(int x,int l,int r,int L,int R,int c){ if(L<=l&&r<=R){ return pre(x,c); } int ret=-inf; if(L<=mid) ret=max(ret,qian(x<<1,l,mid,L,R,c)); if(mid<R) ret=max(ret,qian(x<<1|1,mid+1,r,L,R,c)); return ret; } int hou(int x,int l,int r,int L,int R,int c){ //cout<<" x "<<x<<" l "<<l<<" r "<<r<<endl; if(L<=l&&r<=R){ return bac(x,c); } int ret=inf; if(L<=mid) ret=min(ret,hou(x<<1,l,mid,L,R,c)); if(mid<R) ret=min(ret,hou(x<<1|1,mid+1,r,L,R,c)); return ret; } int kth(int l,int r,int k){ //cout<<" l r k "<<l<<" "<<r<<" "<<k<<endl; int L=0,R=1e8; int ret=0; while(L<=R){ int M=(L+R)/2; //cout<<L<<" "<<R<<" "<<M<<endl; int tmp=pai(1,1,n,l,r,M); if(tmp<k) ret=M,L=M+1; else R=M-1; } return ret; } int main(){ scanf("%d%d",&n,&m); for(reg i=1;i<=n;++i)rd(a[i]); jian(1,1,n); // cout<<" tot "<<cur<<endl; // for(reg i=1;i<=cur;++i){ // cout<<i<<" : "<<t[i].ch[0]<<" "<<t[i].ch[1]<<" "<<t[i].val<<" "<<t[i].fa<<endl; // } int op,l,r,p,k; while(m--){ rd(op); switch(op){ case 1:rd(l);rd(r);rd(k);printf("%d\n",pai(1,1,n,l,r,k)+1);break;//warning!!! +1 case 2:rd(l);rd(r);rd(k);printf("%d\n",kth(l,r,k));break; case 3:rd(p);rd(k);chan(1,1,n,p,a[p],k);a[p]=k;break; case 4:rd(l);rd(r);rd(k);printf("%d\n",qian(1,1,n,l,r,k));break; case 5:rd(l);rd(r);rd(k);printf("%d\n",hou(1,1,n,l,r,k));break; } } return 0; } } int main(){ Miracle::main(); return 0; } /* Author: *Miracle* Date: 2018/11/22 20:22:06 */
动态逆序对。
看下面加强版。
[TJOI2017]不勤劳的图书管理员
类似动态逆序对。
考虑(l+1,r-1)区间内的影响。
要统计小于(大于)a[x](a[y])的个数以及总和。
线段树套平衡树轻而易举。空间O(2*N+NlogN)
线段树套动态开点权值线段树也可以。空间O(2*N+Nlog^2N)
线段树套线段树
一般必然有一个要动态开点。
空间不太优秀:O(nlogn^2)
(但是常数小一些,而且好写)
列出DP式子,发现是三维偏序。
(所以果断CDQ优化DP)
树套树做的话,可以下标线段树套动态开点权值线段树。节点维护dp值最大值
(当然,万能的线段树套平衡树也可以)