主席树(萌新,复习用)

主席树是解决查询历史线段树值的问题的数据结构。

由于改变一个点值时,线段树改变不完全,所以利用一个线段树的空间存储一个历史值是很浪费空间的。

那么此时我们只需要建立一个根节点,对未改变的节点连边,对改变的节点新建节点,连边即可。

本算法思路极其简单,主要看代码实现。

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] 森林(树上主席树)

P3168 [CQOI2015]任务查询系统(小变式)

P4559 [JSOI2018]列队(大变式)

P2633 Count on a tree(树剖主席树)

P3293 [SCOI2016]美味(主席树变式)

P4618 [SDOI2018]原题识别(主席树树剖)

posted @ 2023-02-24 14:02  Gmt丶Fu9ture  阅读(34)  评论(0)    收藏  举报