主席树(静态区间k小值)

参考文献链接: https://blog.youkuaiyun.com/a_forever_dream/article/details/80450549

                          http://www.cnblogs.com/Empress/p/4652449.html

主席树是一种可持久化动态线段树,常用于求区间k小值问题。

其本质为建n棵前缀和线段树,也就是n个版本,第i个版本囊括区间[1~i],这里的i指的是第i个数。那么求第l到第r个数的区间k小时,便可以转化为求query(r)-query(l-1)。

对于一个序列,进行排序和unique去重,得到一个长度为s的元素互不相同的数组,通过其下标离散化减小空间消耗。易得每颗线段树大小都为s,重复数字只需数量加一即可。

unique函数详解:https://www.cnblogs.com/wangkundentisy/p/9033782.html

但这并没有结束,若是对于n<=200000,每个i开一颗线段树,空间复杂度得上天。可以发现,第i棵线段树与第i-1棵并没有太多节点信息改变,实际上最多有logn个节点改变。所以,第i棵线段树可以与第i-1棵线段树共用信息相同的节点,可以省下大量浪费的空间。

附上洛谷P3834 【模板】可持久化线段树 1(主席树)代码:

#include<bits/stdc++.h>
using namespace std;
int tree[200001*40];
int ls[200001*40];
int rs[200001*40];
int rt[200001];
int a[200001];
int b[200001];
int n,m,cnt;
int build(int l,int r){
	int id=++cnt;
	int mid=(l+r)>>1;
	if(l==r) return id;
	ls[id]=build(l,mid);
	rs[id]=build(mid+1,r);
	return id;
}
int update(int pre,int l,int r,int x){
	int id=++cnt;
	ls[id]=ls[pre];
	rs[id]=rs[pre];
	tree[id]=tree[pre]+1;
	if(l==r) return id;
	int mid=(l+r)>>1;
	if(x<=mid) ls[id]=update(ls[pre],l,mid,x);
	else rs[id]=update(rs[pre],mid+1,r,x);
	return id;
}
int query(int LT,int RT,int l,int r,int k){
	if(l>=r) 
	return l;
	int mid=(l+r)>>1;
	int sum=tree[ls[RT]]-tree[ls[LT]];
	if(sum>=k) return query(ls[LT],ls[RT],l,mid,k);
	else return query(rs[LT],rs[RT],mid+1,r,k-sum);	
}
int main(){
	int x,y,k;
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++){
		scanf("%d",&a[i]);
		b[i]=a[i];
	}
	sort(b+1,b+n+1);
	int s=unique(b+1,b+n+1)-b-1;
	rt[0]=build(1,s);
	for(int i=1;i<=n;i++){
		int d=lower_bound(b+1,b+s+1,a[i])-b;
		rt[i]=update(rt[i-1],1,s,d);
	} 
	for(int i=1;i<=m;i++){
		scanf("%d%d%d",&x,&y,&k);
		printf("%d\n",b[query(rt[x-1],rt[y],1,s,k)]);
	}
}

PS:主席树时间复杂度为O((n+m)logs),建树为O(nlogs),修改为O(logs),s为去重后的元素个数。

        空间复杂度为O(nlogn+nlogn),空树O(nlogn),加树(logn)。

动态主席树待续...

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值