可持久化线段树——主席树

主席树学习笔记

往期回顾:
线段树

什么是主席树?

主席树,即可持久化线段树,又称函数式线段树,是重要的可持久化数据结构之一。

主席树,学名可持久化线段树

指在修改线段树的过程中,把线段树的每一种历史版本全都记录下来

在记录每一个版本时,出现变化的点会分裂开,而重复的部分可以直接拿来用

于是形成了一个这样的结构

不同灰度的线连接起的都是这一颗线段树的不同版本

颜色相同的点表示由一个点分裂而来

不同的根节点就代表不同的版本

总之,主席树就是这样一种肥肠简 (du) 单 (liu) 的数据结构

(淦,学他整整花了我 \(3\) 天!)

主席树能解决的问题

主席树的用途其实还挺广的

但由于本蒟蒻对其知之甚少,不够了解主席树

没有总结出他的常见套路

不过总结出了一点东西的:

  • 需要区间内容有序的,一般用主席树
    eg:区间第 \(K\) 小数/区间内至少有 \(x\) 个数大于 \(k\)

  • 想到了线段树的做法,但是要开好多棵线段树,用主席树优化

  • 主席树还经常和前缀和思想离散化 结合

注意:

主席树节点编号不能通过父节点计算出来

需要动态开点,并在结构体中存储左右儿子的节点编号

代码

#include<iostream>

using namespace std;

const int N=1e6+10;

void read(int& x)
{
	x=0;
	int f=1;
	char c;
	c=getchar();
	while(c<'0'||c>'9')
	{
		if(c=='-')
			f=-1;
		c=getchar();
	}
	while(c>='0'&&c<='9')
	{
		x=x*10+c-'0';
		c=getchar();
	}
	x*=f;
}

void write(int x)
{
	if(x<0)
	{
		putchar('-');
		x=-x;
	}
	if(x>9)
		write(x/10);
	putchar(x%10+'0');
}

struct Tree{
	int l,r;
	int v;
}tr[50*N];

int n,m;
int w[N];
int root[N],idx;

int newroot(int u)
{
	tr[++idx]=tr[u];
	return idx;
}

int build(int u,int l,int r)
{
	u=newroot(u);
	if(l==r)
	{
		tr[u].v=w[l];
		return u;
	}
	int mid=l+r>>1;
	tr[u].l=build(tr[u].l,l,mid);
	tr[u].r=build(tr[u].r,mid+1,r);
	return u;
}

int update(int u,int l,int r,int k,int x)
{
	u=newroot(u);
	if(l==r)
	{
		tr[u].v=x;
		return u;
	}
	int mid=l+r>>1;
	if(k<=mid)
		tr[u].l=update(tr[u].l,l,mid,k,x);
	else 
		tr[u].r=update(tr[u].r,mid+1,r,k,x);
	return u;
}

int query(int u,int l,int r,int k)
{
	u=newroot(u);
	if(l==r)
		return tr[u].v;
	int mid=l+r>>1;
	if(k<=mid)
		return query(tr[u].l,l,mid,k);
	else
		return query(tr[u].r,mid+1,r,k);
}

int main()
{
	read(n),read(m);
	for(int i=1;i<=n;i++)
		read(w[i]);
	root[0]=build(0,1,n);
	for(int i=1;i<=m;i++)
	{
		int vn,op,ps,vl;
		read(vn),read(op),read(ps);
		if(op==1)
		{
			read(vl);
			root[i]=update(root[vn],1,n,ps,vl);
		}else{
			write(query(root[vn],1,n,ps));
			puts("");
			root[i]=root[vn];
		}
	}
	
	return 0;
}
#include<iostream>
#include<algorithm>
#include<vector>

using namespace std;

const int N=200020;

struct Tree{
	int l,r;
	int cnt;
}tr[N*22];

int n,m;
int root[N],idx;
int a[N];
vector<int> nums;

int find(int x)
{
	return lower_bound(nums.begin(),nums.end(),x)-nums.begin();
}

int build(int l,int r)
{
	int p=++idx;
	if(l==r)
		return p;
	int mid=l+r>>1;
	tr[p].l=build(l,mid);
	tr[p].r=build(mid+1,r);
	return p;
}

int update(int p,int l,int r,int k)
{
	int q=++idx;
	tr[q]=tr[p];
	if(l==r)
	{
		tr[q].cnt++;
		return q;
	}
	int mid=l+r>>1;
	if(k<=mid)
		tr[q].l=update(tr[p].l,l,mid,k);
	else
		tr[q].r=update(tr[p].r,mid+1,r,k);
	tr[q].cnt=tr[tr[q].l].cnt+tr[tr[q].r].cnt;
	return q;
}

int query(int p,int q,int l,int r,int k)
{
	if(l==r)
		return l;
	int cnt=tr[tr[p].l].cnt-tr[tr[q].l].cnt; 
	int mid=l+r>>1;
	if(k<=cnt)
		return query(tr[p].l,tr[q].l,l,mid,k);
	else 
		return query(tr[p].r,tr[q].r,mid+1,r,k-cnt);
}

int main()
{
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++)
	{
		scanf("%d",&a[i]);
		nums.push_back(a[i]);
	}
	sort(nums.begin(),nums.end());
	nums.erase(unique(nums.begin(),nums.end()),nums.end());
	root[0]=build(0,nums.size()-1);
	for(int i=1;i<=n;i++)
		root[i]=update(root[i-1],0,nums.size()-1,find(a[i]));
	while(m--)
	{
		int l,r,k;
		scanf("%d%d%d",&l,&r,&k);
		printf("%d\n",nums[query(root[r],root[l-1],0,nums.size()-1,k)]);
	}
	
	return 0;
}

结束啦~

主席树学习笔记就写到这里

还需要继续练题呀~!

加油💪哈

白白ヾ(•ω•`)o ~~~

posted @ 2022-08-18 16:28  OrangeStar*  阅读(44)  评论(0编辑  收藏  举报