可持久化线段树
可持久化线段树
可持久化线段树最大的特点是:可以访问历史版本。
考虑一棵普通的线段树,进行单点修改,每次修改会改变 \(\log n\)个点。
于是每次修改的时候,不修改原来的节点,而是在它旁边新建一个节点,把原来节点的信息(如左右儿子编号、区间和等)复制到新节点上,并对新节点进行修改。
对于查询历史版本,只需记录每一次修改对应的新根节点编号,每次询问从对应的根节点往下查询就可以了。
所以我们只要在记录左右儿子的基础上,保存插入每个数的时候的根节点就可以实现持久化了。
空间 \(O((n+m)\log n)\),一般开 N<<5
主席树
主席树全称是可持久化权值线段树,主要用来求区间第 \(k\) 小
主要思想是每个位置都维护一个线段树,线段树的节点是值的范围,然后第 \(i\) 个线段树中某个区间 \([x, y]\) 维护的是 \(1 \sim i\) 位置上的数字在 \([x, y]\) 范围内的个数。
利用前缀和的思想,如果需要得到 \([l,r]\) 的统计信息,只需要用 \([1,r]\) 的信息减去 \([1,l - 1]\) 的信息就行了,即第 \(r\) 棵线段树减去第 \(l-1\) 棵线段树。
\(insert\)
il void insert(int &rt,cs int l,cs int r,cs int k){
t[++id]=t[rt],rt=id,t[rt].sum++;
if(l>=r) return;
int mid=(l+r)>>1;
if(k<=mid) insert(t[rt].ls,l,mid,k);
else insert(t[rt].rs,mid+1,r,k);
return;
}
\(ask\)
il int ask(int ql,int qr,int l,int r,int k){
if(l==r) return l;
int mid=(l+r)>>1,lsz=t[t[qr].ls].sum-t[t[ql].ls].sum;
if(lsz>=k) return ask(t[ql].ls,t[qr].ls,l,mid,k);
else return ask(t[ql].rs,t[qr].rs,mid+1,r,k-lsz);
}
code
#include<bits/stdc++.h>
#define il inline
#define cs const
#define ri register
using namespace std;
namespace Q{
il int rd(){
ri int x=0;ri bool f=0;ri char c=getchar();
while(!isdigit(c)) f|=(c=='-'),c=getchar();
while(isdigit(c))x=x*10+(c^48),c=getchar();
return f?-x:x;
}
il void wt(int x){
if(x<0) x=-x,putchar('-');
if(x>=10) wt(x/10);
return putchar(x%10+48),void();
}
} using namespace Q;
cs int N=2e5+5,inf=0x3f3f3f3f;
int n,m,rot[N],arr[N],b[N];
struct qaq{
int num,id;
bool operator<(cs qaq o)cs{
return num<o.num;
}
}a[N];
namespace Tree{
int id;
struct qwq{
int ls,rs,sum;
}t[N<<5];
il void insert(int &rt,cs int l,cs int r,cs int k){
t[++id]=t[rt],rt=id,t[rt].sum++;
if(l>=r) return;
int mid=(l+r)>>1;
if(k<=mid) insert(t[rt].ls,l,mid,k);
else insert(t[rt].rs,mid+1,r,k);
return;
}
il int ask(int ql,int qr,int l,int r,int k){
if(l==r) return l;
int mid=(l+r)>>1,lsz=t[t[qr].ls].sum-t[t[ql].ls].sum;
if(lsz>=k) return ask(t[ql].ls,t[qr].ls,l,mid,k);
else return ask(t[ql].rs,t[qr].rs,mid+1,r,k-lsz);
}
} using namespace Tree;
signed main(){
n=rd(),m=rd();
for(ri int i=1;i<=n;++i) a[i]={rd(),i};
sort(a+1,a+1+n);
int len=0;a[0].num=inf;
for(ri int i=1;i<=n;++i){
if(a[i].num!=a[i-1].num){
arr[++len]=a[i].num;
}
b[a[i].id]=len;
}
for(ri int i=1;i<=n;++i){
rot[i]=rot[i-1];
insert(rot[i],1,len,b[i]);
}
for(ri int i=1,l,r,k;i<=m;++i){
l=rd(),r=rd(),k=rd();
wt(arr[ask(rot[l-1],rot[r],1,len,k)]);
putchar('\n');
}
return 0;
}
I went to the woods because I wanted to live deliberately, I wanted to live deep and suck out all the marrow of life, and not when I had come to die, discover that I had not live.