主席树(萌新,复习用)
主席树是解决查询历史线段树值的问题的数据结构。
由于改变一个点值时,线段树改变不完全,所以利用一个线段树的空间存储一个历史值是很浪费空间的。
那么此时我们只需要建立一个根节点,对未改变的节点连边,对改变的节点新建节点,连边即可。
本算法思路极其简单,主要看代码实现。
void build(int &x,int l,int r)
{
if(!x)x=++cnt;
if(l==r)
{
f[x].data=a[l];
return;
}
int mid=(l+r)>>1;
build(f[x].ls,l,mid);
build(f[x].rs,mid+1,r);
}
void update(int &x,int y,int l,int r)
{
if(!x)x=++cnt;
if(l==r)
{
f[x].data=k;
return;
}
f[x]=f[y];
int mid=(l+r)>>1;
if(mid>=nl)update(f[x].ls=0,f[y].ls,l,mid);
else update(f[x].rs=0,f[y].rs,mid+1,r);
}
应用:解决区间第 \(k\) 小问题。
每加入一个数,建立一个版本的线段树,最后利用前缀和求解第 \(k\) 小。
void update(int &x,int y,int l,int r)
{
if(!x)x=++cnt;
if(l==r)
{
f[x].data=f[y].data+1;
return;
}
int mid=(l+r)>>1;
f[x]=f[y];
if(mid>=k)update(f[x].ls=0,f[y].ls,l,mid);
else update(f[x].rs=0,f[y].rs,mid+1,r);
pushup(x);
}
int search(int x,int y,int l,int r)
{
if(l==r)return t[l];
int sum=f[f[y].ls].data-f[f[x].ls].data;
int mid=(l+r)>>1;
if(k<=sum)return search(f[x].ls,f[y].ls,l,mid);
k-=sum;
return search(f[x].rs,f[y].rs,mid+1,r);
}
思路仍然非常简单,此数据结构注重于应用,所以还要多练习。
P3919 【模板】可持久化线段树 1(可持久化数组)(纯模板)
P3834 【模板】可持久化线段树 2(应用模板)
P3567 [POI2014]KUR-Couriers(简单应用)
P2468 [SDOI2010]粟粟的书架(主席树练习题,合二为一?)
P3302 [SDOI2013] 森林(树上主席树)
P4559 [JSOI2018]列队(大变式)
P2633 Count on a tree(树剖主席树)
P3293 [SCOI2016]美味(主席树变式)
P4618 [SDOI2018]原题识别(主席树树剖)

浙公网安备 33010602011771号