*主席树学习笔记
未完待续
主席树
先来看一个问题:给你一个数列,让你求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