树套树(下标线段树+权值平衡树)

P3380 【模板】二逼平衡树(树套树)
一天写一题,一题写一天

看了看题解,这道题的做法太太太多了

有整体二分,cdq,分块,树套树还有乱搞?

整体二分,cdq我不会,就不谈了

先说分块:时间复杂度n1.5log2,容易被卡(虽然这道题没有被卡)

树套树(下标线段树+权值平衡树):时间复杂度:nlog3

树套树(权值线段树+下标平衡树):时间复杂度:nlog2

因为是第一次写树套树的题目,用了非常久的时间

所谓树套树(就该题而言),就是在每一个线段树的节点中建一颗平衡树,然后维护每一个节点对应的平衡树。

具体建树方式如下图:

在这里插入图片描述

修改:修改一个区间上的一个点。需要修改修改线段树的一条链所对应的平衡树,要先插入后删除,最后还要修改原数组

建树:建树是按照下标线段树建立的,对于与一个下标,要往平衡树插入对应区间内的所有值

查询:除了第k大的查询都比较相似,就说一下第k大的查询

这个查询是采取的二分形式,二分查询的值,时间复杂度来源于二分+线段树查询+平衡树查询都是log级的,所以时间复杂度log3

#include <bits/stdc++.h>
#define inf 0x7fffffff
//#define ll long long
//#define int long long
//#define double long double
//#define double long long
#define re int
//#define void inline void
#define eps 1e-8
//#define mod 1e9+7
//#define ls(p) p<<1
//#define rs(p) p<<1|1
#define pi acos(-1.0)
#define pb push_back
#define mk make_pair
#define P pair < int , int >
using namespace std;
const int mod=1e9+7;
//const int inf=1e18;
const int M=1e8;
const int N=5e4+5;//??????.???? 4e8
struct fhq
{
	int l,r,sz,val,key;
}e[N*50];
int tot;
int newnode(int val)
{
	e[++tot].val=val;
	e[tot].key=rand();
	e[tot].sz=1;
	return tot;
} 
void update(int p)
{
	e[p].sz=e[e[p].l].sz+e[e[p].r].sz+1;
}
void split(int p,int val,int &x,int &y)
{
	if(!p)  x=y=0;
	else
	{
		if(e[p].val<=val)
		{
			x=p;
			split(e[p].r,val,e[x].r,y);//
			update(x);
		}
		else
		{
			y=p;
			split(e[p].l,val,x,e[y].l);//
			update(y);
		}
//		update(p);
	}
}
void merge(int &p,int x,int y)
{
	if(!x||!y)
	{
		p=x+y;
		return;
	}
	if(e[x].key<e[y].key)
	{
		p=x;//
		merge(e[p].r,e[x].r,y);//
//		update(x);
	}
	else
	{
		p=y;//
		merge(e[p].l,x,e[y].l);//
//		update(y);
	}
	update(p);//这里要注意 
}
void insert(int &p,int val)
{
	int x,y;
	split(p,val-1,x,y);
	merge(p,x,newnode(val));
	merge(p,p,y);
}
void remove(int &p,int val)
{
	int x,y,z,w;
	split(p,val-1,x,y);
	split(y,val,z,w);
	merge(z,e[z].l,e[z].r);
	merge(z,z,w);
	merge(p,x,z);
}
int getrank(int &p,int val)
{
	int x,y;
	split(p,val-1,x,y);
	int ans=e[x].sz;
	merge(p,x,y);
	return ans;
}
int last(int p,int val)
{
	int ans=INT_MIN+1;
	while(p)
	{
		if(val>e[p].val)
		{
			ans=max(ans,e[p].val);
			p=e[p].r;
		}
		else  p=e[p].l;
	}
	return ans;
}
int next(int p,int val)
{
	int ans=INT_MAX;
	while(p)
	{
		if(val<e[p].val)
		{
			ans=min(ans,e[p].val);
			p=e[p].l;
		}
		else  p=e[p].r;
	}
	return ans;
}
int kth(int p,int rank)
{
	while(p)
	{
		if(rank<e[e[p].l].sz)  p=e[p].l;
		else if(rank>=e[e[p].l].sz+1)
		{
			rank-=e[e[p].l].sz+1;
			p=e[p].r;
		}
		else  return e[p].val;
	}
	return 0;
}
struct tree
{
	int l,r,lc,rc;
	int rt;
}t[N*4];
int root;
int cnt; 
int a[N];
void bulid(int &p,int l,int r)
{
	p=++cnt;
	t[p].l=l,t[p].r=r;
	for(re i=l;i<=r;i++)  insert(t[p].rt,a[i]);
	if(l==r)  return;
	int mid=(l+r)>>1;
	bulid(t[p].lc,l,mid);bulid(t[p].rc,mid+1,r);
}
void change(int p,int pos,int val)
{
	insert(t[p].rt,val);
	remove(t[p].rt,a[pos]);
	if(t[p].l==t[p].r)  return;
	int mid=(t[p].l+t[p].r)>>1;
	if(pos<=mid)  change(t[p].lc,pos,val);
	else  change(t[p].rc,pos,val);
}
int qrank(int p,int l,int r,int val)
{
	if(t[p].l==l&&t[p].r==r)  return getrank(t[p].rt,val);
	int mid=(t[p].l+t[p].r)>>1; 
	if(r<=mid)  return qrank(t[p].lc,l,r,val);
	else if(l>=mid+1)  return qrank(t[p].rc,l,r,val);
	return qrank(t[p].lc,l,mid,val)+qrank(t[p].rc,mid+1,r,val);
}
int qlast(int p,int l,int r,int val)
{
	if(t[p].l==l&&t[p].r==r)  return last(t[p].rt,val);
	int mid=(t[p].l+t[p].r)>>1;
	if(r<=mid)  return qlast(t[p].lc,l,r,val); 
	else if(l>=mid+1)  return qlast(t[p].rc,l,r,val);
	return max(qlast(t[p].lc,l,mid,val),qlast(t[p].rc,mid+1,r,val));
}
int qnext(int p,int l,int r,int val)
{
	if(t[p].l==l&&t[p].r==r)  return next(t[p].rt,val);
	int mid=(t[p].l+t[p].r)>>1;
	if(r<=mid)  return qnext(t[p].lc,l,r,val); 
	else if(l>=mid+1)  return qnext(t[p].rc,l,r,val);
	return min(qnext(t[p].lc,l,mid,val),qnext(t[p].rc,mid+1,r,val));
}
int qkth(int p,int l,int r,int rank)
{
	int ll=0,rr=1e8;
	while(rr-ll>1)
	{
		int mid=(ll+rr)>>1;
		if(qrank(root,l,r,mid)<rank)  ll=mid;
		else  rr=mid;
	}
	if(qrank(root,l,r,rr)<rank)  return rr;
	else return ll;
}
int n,q;
void solve()
{
	cin>>n>>q;
	for(re i=1;i<=n;i++)  scanf("%d",&a[i]);
	bulid(root,1,n);
	while(q--)
	{
		int op,l,r,pos,x;
		scanf("%d",&op);
		if(op==3)
		{
			scanf("%d%d",&pos,&x);
			change(root,pos,x);
			a[pos]=x;
		}
		else
		{
			scanf("%d%d%d",&l,&r,&x);
			if(op==1)  printf("%d\n",qrank(root,l,r,x)+1);
			if(op==2)  printf("%d\n",qkth(root,l,r,x));
			if(op==4)  printf("%d\n",qlast(root,l,r,x));
			if(op==5)  printf("%d\n",qnext(root,l,r,x));
		}
	}
} 
signed main() 
{
    int T=1;       
//    cin>>T;
    for(int index=1;index<=T;index++)
    {
        solve();
//        puts("");
    }
    return 0;
}
/*



*/


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值