天天看点

可持久化数组再理解

吐槽:

今天是集训的最后一天,也不知道该学什么,就再复习一下可持久化的思想,又鞭尸了一遍可持久化数组。

可持久化思想:

对于每次修改的数组我们建立一个新版本,因为我们是动态开点式,对于可以利用的点进行利用,所以空间方面会节省很大。我们将上一版本的树复制一遍,将没有利用到的点省去重新开这个点所多出的空间,用图表示也就是将新开的一条链连上上一个没有用到的点,形成的一颗新树

对于多版本​

​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];
        }
    }
}