*主席树学习笔记

未完待续

主席树

先来看一个问题:给你一个数列,让你求n次区间第k小值(题目链接

我们想想:

第x棵线段树减去第y棵线段树会发生什么?

第x棵线段树代表的区间是[1,x]

第y棵线段树代表的区间是[1,y]

两棵线段树一减

设x>y,\([1,x]−[1,y]=[y+1,x]\)

所以这两棵线段树相减可以产生一个新的区间对应的线段树!

所以,我们只需要建n棵权值线段树(\([1,i]\))就可以表示出每一个区间了!

但是又会有一个问题:空间爆了

我们会发现

上代码

#include<bits/stdc++.h>
#define N 220000
#define LC t[rt].lc
#define RC t[rt].rc
#define _LC t[_rt].lc
#define _RC t[_rt].rc
using namespace std;
int n,m,x,y,k,cnt,size,a[N],b[N],c[N],rt[N];
struct tree{
	int sum,lc,rc;
}t[N<<5];
void updata(int &rt,int _rt,int l,int r,int k){
	rt=++cnt;
	t[rt]=t[_rt];
	++t[rt].sum;
	if(l==r)return;
	int mid=(l+r)>>1;
	if(k<=mid)updata(LC,_LC,l,mid,k);
	else updata(RC,_RC,mid+1,r,k);
}
int query(int rt,int _rt,int l,int r,int k){
	if(l==r)return l;
	int mid=(l+r)>>1,v=t[_LC].sum-t[LC].sum;
	if(k<=v)return query(LC,_LC,l,mid,k);
	else return query(RC,_RC,mid+1,r,k-v);
}
int main(){
	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);
	size=unique(b+1,b+n+1)-b-1;
	for(int i=1;i<=n;i++)updata(rt[i],rt[i-1],1,size,lower_bound(b+1,b+size+1,a[i])-b);
	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,size,k)]);
	}
}

怎么感觉比普通线段树还短

参考资料:https://www.cnblogs.com/hanruyun/p/9916299.html
https://blog.csdn.net/ModestCoder_/article/details/90107874

posted @ 2020-07-09 16:30  ZTC_ZTC  阅读(163)  评论(1编辑  收藏  举报