可持久化数据结构总结

1.可持久化数组

这个是众多可持久化数据结构的基础,实现方式也是最基础的,只需要用到主席树即可

需要维护这样的一个长度为n的数组,支持如下几种操作

  1. 在某个历史版本上修改某一个位置上的值

  2. 访问某个历史版本上的某一位置的值

我们发现更新线段树的时候,每层仅仅修改了一个点,所以我们记录新的版本不需要把整棵树复制一遍,而是只需要把更新的节点新建出来即可 

【代码】

int build(int now,int l,int r)
{
	now=++cnt;
	if(l==r)
	{
		tr[now].val=a[l];
		return cnt;
	}
	int mid=(l+r)>>1;
	tr[now].l=build(tr[now].l,l,mid);
	tr[now].r=build(tr[now].r,mid+1,r);
	return now;
}
int update(int now,int l,int r,int x,int val)
{
	cnt++; tr[cnt]=tr[now]; now=cnt;
	if(l==r)
	{
		tr[now].val=val;
		return now;
	}
	int mid=(l+r)>>1;
	if(x<=mid) tr[now].l=update(tr[now].l,l,mid,x,val);
	else tr[now].r=update(tr[now].r,mid+1,r,x,val);
	return now;
}
int query(int now,int l,int r,int x)
{
	if(l==r) return tr[now].val;
	int mid=(l+r)>>1;
	if(x<=mid) return query(tr[now].l,l,mid,x);
	else return query(tr[now].r,mid+1,r,x);
}
int main()
{
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++) scanf("%d",&a[i]);
	root[0]=build(0,1,n);
	int x,y,z,op;
	for(int i=1;i<=m;i++)
	{
		scanf("%d%d",&x,&op);
		if(op==1)
		{
			scanf("%d%d",&y,&z);
			root[i]=update(root[x],1,n,y,z);
		}
		else
		{
			scanf("%d",&y);
			printf("%d\n",query(root[x],1,n,y));
			root[i]=root[x];
		}
	}
	return 0;
}

2.可持久化并查集

我们要想维护历史版本的fa数组,自然会想到可持久化数组(利用主席树实现),这里的并查集就不能使用路径压缩了,只能使用按秩合并来保证复杂度

【代码】

int build(int now,int l,int r)
{
	now=++cnt;
	if(l==r)
	{
		f[now]=l;
		return now;
	}
	int mid=(l+r)>>1;
	tr[now].l=build(tr[now].l,l,mid);
	tr[now].r=build(tr[now].r,mid+1,r);
	return now;
}
void update(int &now ,int pre,int l,int r,int x,int val)
{
	now=++cnt;
	if(l==r)
	{
		f[now]=val;
		dep[now]=dep[pre];
		return;
	}
	tr[now].l=tr[pre].l;
	tr[now].r=tr[pre].r;
	int mid=(l+r)>>1;
	if(x<=mid) update(tr[now].l,tr[pre].l,l,mid,x,val);
	else update(tr[now].r,tr[pre].r,mid+1,r,x,val);
}
int query(int now,int l,int r, int x)
{
	if(l==r) return now;
	int mid=(l+r)>>1;
	if(x<=mid) return query(tr[now].l,l,mid,x);
	else return query(tr[now].r,mid+1,r,x);
}
int find(int now,int x)
{
	int father=query(now,1,n,x);
	if(x==f[father]) return father;
	return find(now,f[father]);
}
void add(int now,int l,int r,int x) //使得now中每个点的深度+1
{
	if(l==r)
	{
		dep[now]++;
		return;
	}
	int mid=(l+r)>>1;
	if(x<=mid) add(tr[now].l,l,mid,x);
	else add(tr[now].r,mid+1,r,x);
}
int main()
{
   // freopen("a.in","r",stdin);
  //  freopen("a.out","w",stdout);
    int op,x,y;
    scanf("%d%d",&n,&m);
    root[0]=build(0,1,n);
	for(int i=1;i<=m;i++)
	{
		scanf("%d",&op);
		if(op==1)
		{
			scanf("%d%d",&x,&y);
			root[i]=root[i-1];
			int f1=find(root[i],x),f2=find(root[i],y);
			if(f[f1]==f[f2]) continue;
			if(dep[f1]>dep[f2]) swap(f1,f2);
			update(root[i],root[i-1],1,n,f[f1],f[f2]);
			if(dep[f1]==dep[f2]) add(root[i],1,n,f[f2]);
		}
		if(op==2)
		{
			scanf("%d",&x);
			root[i]=root[x];
		}
		if(op==3)
		{
			root[i]=root[i-1];
			scanf("%d%d",&x,&y);
			int f1=find(root[i],x),f2=find(root[i],y);
			if(f[f1]==f[f2]) printf("1\n");
			else printf("0\n");
		}
	}
    return 0;
}

3.可持久化Trie

        建立历史版本的前缀Trie树即可

        P4735 最大异或和 

        需要找到一个数x,异或[l,r]中一个数的最大值,建立前缀版本的Trie,每次贪心的时候比较一下版本编号是否可行即可

        P4585 [FJOI2015]火星商店问题 

        这道题目需要对时间和空间两个维度处理,首先看到了异或最大,想到了用01trie,又因为有区间l-r的范围,需要可持久化trie。因为有时间d的限制,我们需要用线段树分治来处理,考虑一个商品影响到的时间跨度为放入到最后,我们把询问插入线段树,按照线段树的分治思想计算每个部分的答案,并最终贡献到询问中。 代码

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值