树套树

树套树

树状数组,(动态开点线段树),平衡树

二逼平衡树

您需要写一种数据结构(可参考题目标题),来维护一个有序数列,其中需要提供以下操作:

  1. 查询 \(k\) 在区间内的排名

  2. 查询区间内排名为 \(k\) 的值

  3. 修改某一位值上的数值

  4. 查询
    在区间内的前驱(前驱定义为小于
    ,且最大的数)

  5. 查询 \(k\) 在区间内的后继(后继定义为大于 \(k\),且最小的数)

  • 区间问题

  • 前驱后继

使用线段树处理区间,每个区间维护平衡树 \(O(N\log^2 N+M\log^3 N)\)

树状数组套主席树 \(O((N+M)\log^2 N)\)

两种数据需要开两个结构体

拆到两个数据结构结合起来,取他们的优点

平衡树天然动态开点

#include<bits/stdc++.h>
using namespace std;
const int N=50010;
const int V=1e8;
int n,m;
#define INF 0x3f3f3f3f
int tot;
constexpr int L1=16,L2=27; 
struct SEG_Node{
	int ls,rs,sum;
	#define lc t[p].ls
	#define rc t[p].rs
	#define mid (L+R>>1)
}t[N*L1*L2];
int mknode(){
	++tot;
	return tot;
}
int a[N];
struct SEG{
	int rt;
	void pushup(int p){
		t[p].sum = t[lc].sum+t[rc].sum;
	}
	void modify(int &p,int L,int R,int x,int v){
		if(!p)p=mknode();
		if(L==R){
			t[p].sum += v;
			return;
		}
		if(x<=mid)modify(lc,L,mid,x,v);
		else modify(rc,mid+1,R,x,v);
		pushup(p);
	}
};
struct BIT{
	SEG c[N];
	#define lowbit(x) ((x)&-(x))
	void change(int x,int v,int k){
		if(x==0)return;
		for(int i=x;i<=n;i+=lowbit(i))
			c[i].modify(c[i].rt,0,V,v,k);
	}
	void find(int x,vector<int> &a){
		for(;x;x-=lowbit(x))a.emplace_back(c[x].rt);
	}
}bit;
int lsum(vector<int> a,vector<int> b){
	int res=0;//可能一个节点会变空
	for(auto x:a)if(x&&t[x].ls)res -= t[t[x].ls].sum;
	for(auto x:b)if(x&&t[x].ls)res += t[t[x].ls].sum;
	return res;
}
int rsum(vector<int> a,vector<int> b){
	int res=0;//可能一个节点会变空
	for(auto x:a)if(x&&t[x].rs)res -= t[t[x].rs].sum;
	for(auto x:b)if(x&&t[x].rs)res += t[t[x].rs].sum;
	return res;
}
void goL(vector<int> &a,vector<int> &b){
	for(int i=0;i<a.size();++i)if(a[i])a[i]=t[a[i]].ls;
	for(int i=0;i<b.size();++i)if(b[i])b[i]=t[b[i]].ls;
}
void goR(vector<int> &a,vector<int> &b){
	for(int i=0;i<a.size();++i)if(a[i])a[i]=t[a[i]].rs;
	for(int i=0;i<b.size();++i)if(b[i])b[i]=t[b[i]].rs;
}
int krank(int l,int r,int k){
	vector<int> a,b;
	a.clear();a.shrink_to_fit();
	b.clear();b.shrink_to_fit();
	bit.find(l-1,a);//当前的节点
	bit.find(r,b);//当前的节点
	int L=0,R=V;
	int res=1;
	while(L<=R){
		if(L==R)return res;
		if(k>mid)res += lsum(a,b);
		if(k<=mid)goL(a,b),R=mid;
		else goR(a,b),L=mid+1;
	}
}
int kth(int l,int r,int k){
	vector<int> a,b;
	a.clear();a.shrink_to_fit();
	b.clear();b.shrink_to_fit();
	bit.find(l-1,a);//当前的节点
	bit.find(r,b);//当前的节点
	int L=0,R=V;
	int res=0;
	while(L<=R){
		if(L==R)return L;
		int tmp=lsum(a,b);
		if(k<=tmp)R=mid,goL(a,b);
		else L=mid+1,k-=tmp,goR(a,b);
	}
}
int pre(int l,int r,int k){
	int rk=krank(l,r,k);
	return rk==1?-2147483647:kth(l,r,rk-1);
}
int suf(int l,int r,int k){
	int rk=krank(l,r,k+1);
	return rk==r-l+2?2147483647:kth(l,r,rk);
}
int main(){
	ios::sync_with_stdio(0); 
	cin>>n>>m;
	for(int i=1;i<=n;++i){
		cin>>a[i];
		bit.change(i,a[i],1);
	}
	for(int i=1;i<=m;++i){
		int opt;cin>>opt;
		if(opt==1){
			int l,r,k;
			cin>>l>>r>>k;
			printf("%d\n",krank(l,r,k));
		}else if(opt==2){
 			int l,r,k;
 			cin>>l>>r>>k;
 			printf("%d\n",kth(l,r,k));
		}else if(opt==3){
			int pos,k;cin>>pos>>k;
			bit.change(pos,a[pos],-1);
			a[pos]=k;
			bit.change(pos,a[pos],1);
		}else if(opt==4){
			int l,r,k;
			cin>>l>>r>>k;
			printf("%d\n",pre(l,r,k));
		}else{
			int l,r,k;
			cin>>l>>r>>k;
			printf("%d\n",suf(l,r,k));
		}
	}
	return 0;
}

K大数查询

权值线段树套线段树,线段树套权值线段树,都可以。

#include<bits/stdc++.h>
using namespace std;
#define ll long long 
int n,m;
const int N=5e4+10,L=32;
constexpr long long V=N+N;
struct Seg_Node{
	int ls,rs;
	ll sum,laz;
	#define lc(p) t[p].ls
	#define rc(p) t[p].rs
	#define mid (L+R>>1)
}t[N*L*L];
int tot;
struct Seg{
	int rt,ls,rs;
	int mknode(){
		return ++tot;
	}
	void pushup(int p){
		t[p].sum = t[lc(p)].sum+t[rc(p)].sum;
	}
	void op(int p,int v1,int v2){
		t[p].sum += v1;
		t[p].laz += v2;
	}
	void pushdown(int p,int L,int R){
		if(!t[p].ls)t[p].ls=mknode();
		if(!t[p].rs)t[p].rs=mknode();
		if(t[p].laz){
			op(lc(p),t[p].laz*(mid-L+1),t[p].laz);
			op(rc(p),t[p].laz*(R-mid),t[p].laz);
			t[p].laz=0;
		}
	}
	void modify(int &p,int L,int R,int l,int r,int v){
		if(!p)p=mknode();
		if(l<=L&&R<=r){
			op(p,(R-L+1)*v,v);
			return;
		}
		pushdown(p,L,R);
		if(l<=mid)modify(lc(p),L,mid,l,r,v);
		if(r>mid)modify(rc(p),mid+1,R,l,r,v);
		pushup(p);
	}
	ll query(int p,int L,int R,int l,int r){
		if(rt==0)return 0;
		if(l<=L&&R<=r)return t[p].sum;
		pushdown(p,L,R);
		ll res=0;
		if(l<=mid)res+=query(lc(p),L,mid,l,r); 
		if(r>mid)res+=query(rc(p),mid+1,R,l,r);
		return res;
	} 
}segt[N*L];
int TOT;
struct SEG{
	int rt;
	int mknode(){
		return ++TOT;
	}
	void change(int &p,int L,int R,int x,int l,int r){
		if(!p)p=mknode();
		segt[p].modify(segt[p].rt,1,n,l,r,1);
		// printf("rttrtrt %d %d\n",p,segt[p].rt);
		if(L==R)return;
		if(x<=mid)change(segt[p].ls,L,mid,x,l,r);
		else change(segt[p].rs,mid+1,R,x,l,r);
	}
	int query(int p,int L,int R,ll x,int l,int r){
		if(L==R)return L;
		int Rs=segt[p].rs;
		ll cntr=Rs?segt[Rs].query(segt[Rs].rt,1,n,l,r):0;
		if(x<=cntr)return query(segt[p].rs,mid+1,R,x,l,r);
		else return query(segt[p].ls,L,mid,x-cntr,l,r);
	}
}Seg;
int main(){
	cin>>n>>m;
	for(int i=1;i<=m;++i){
		int opt,l,r;ll c;
		cin>>opt>>l>>r>>c;
		if(opt==1)
			Seg.change(Seg.rt,0,V,c+n,l,r);
		else
			cout<<Seg.query(Seg.rt,0,V,c,l,r)-n<<'\n';
	}
	return 0;
}

C++语言技巧

使用结构体

思想总结

问题的解决使用树套树的前提下,可以有多种解决方式,多种数据结构的套也许都能解决问题,那么就要进行综合复杂度分析。

树套树的本质是利用多种树据结构的优势,比如第一题,既使用BIT进行区间查询,又使用树桶实现BST功能

posted @ 2024-02-25 22:21  妖灵梦  阅读(26)  评论(0)    收藏  举报