吐槽:
今天是集训的最后一天,也不知道该学什么,就再复习一下可持久化的思想,又鞭尸了一遍可持久化数组。
可持久化思想:
对于每次修改的数组我们建立一个新版本,因为我们是动态开点式,对于可以利用的点进行利用,所以空间方面会节省很大。我们将上一版本的树复制一遍,将没有利用到的点省去重新开这个点所多出的空间,用图表示也就是将新开的一条链连上上一个没有用到的点,形成的一颗新树
对于多版本root[]
数组的理解
对于刚刚学习可持久化主席树,或者可持久化数组,我们都会见到一个
root[ ]
,像我一样的菜鸡一定很困惑为什么这样就能新开一个新的版本,因为root[ ]
数组保存的是每一个版本的根节点,我们通过根节点向左向右查询就能找到某一版本的值,因为从根建立,从根查询是一样的原理,由于建立出来了,所以查询根据同样原理查询即可
手撕代码:
#include<bits/stdc++.h>
using namespace std;
//可持久化并查集
const int maxn =1e6+5;
int n,m,cnt;
struct node
{
int l,r,val;
} tr[maxn*40];
int a[maxn];
int root[maxn];
void build(int &now,int l,int r)//建第一个版本的数组
{
now=++cnt;
if(l==r)
{
tr[now].val=a[l];
return ;
}
int mid=(l+r)>>1;
build(tr[now].l,l,mid);
build(tr[now].r,mid+1,r);
}
void modify(int ver,int &now,int l,int r,int pos,int w)//修改版本
{
now=++cnt;
tr[now]=tr[ver];
if(l==r)
{
tr[now].val=w;
return ;
}
int mid=l+r>>1;
if(mid>=pos)
{
modify(tr[ver].l,tr[now].l,l,mid,pos,w);
}
else
{
modify(tr[ver].r,tr[now].r,mid+1,r,pos,w);
}
}
int query(int ver,int l,int r,int pos)
{
if(l==r)
{
return tr[ver].val;
}
int mid=l+r>>1;
if(mid>=pos)
{
return query(tr[ver].l,l,mid,pos);
}
else
{
return query(tr[ver].r,mid+1,r,pos);
}
}
int main()
{
scanf("%d %d",&n,&m);
for(int i=1; i<=n; i++)
scanf("%d",&a[i]);
build(root[0],1,n);
for(int i=1; i<=m; i++)
{
int ver,op,pos,w;
scanf("%d %d",&ver,&op);
if(op==1)
{
scanf("%d %d",&pos,&w);
modify(root[ver],root[i],1,n,pos,w);
}
if(op==2)
{
scanf("%d",&pos);
printf("%d\n",query(root[ver],1,n,pos));
root[i]=root[ver];
}
}
}