可持久化数组:洛谷3919

最近一直在搞数据结构,主席树,理解也不算特别深刻,还没有对动态开点和可持久化思想理解特别深刻,所以,今天我就准备写一篇可持久化数组来通一通脑子

新手入门题目:洛谷:可持久化数组

就题论题:

给一个数组a[ ]:我们可以对其进行修改,然后询问不同版本的值,最初的版本为:0,修改一次就产生一个新版本

朴素做法:

写一个二维数组ver[i][j] i 代表版本,然后寻问 第 i 个版本第pos个位置的值,这样对于小数据来说还行,不会MLE,也不会TLE,但是对于大数据来说,每次修改都是O(n),修改m次,时间复杂度为O(nm),而且大数据会MLE,ver[10^6][10^4]毫无疑问数组存不下,那么现在可持久化数组出现了,可持久化数组的实现利用了线段树+可持久化思想

首先:
我们建出最初版本的线段树来存版本0的数组,我们以数组大小为5为例:
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

根据上图,已经把图建了出来了,也不怎么耗费空间了,我们要查某一个版本的数组的值,只需要,知道某一版本的根节点即可

AC代码:

#include<bits/stdc++.h>
using namespace std;
//可持久化数组
const int maxn=1e6+5;
int root[maxn];
struct node
{
    int l,r,val;

}tr[maxn*40];
int a[maxn];
int cnt;
void build(int l,int r,int &now)
{
    now=++cnt;
    if(l==r)
    {
        tr[now].val=a[l];
        return;
    }
    int mid=l+r>>1;
    build(l,mid,tr[now].l);
    build(mid+1,r,tr[now].r);
}
void modify(int l,int r,int per,int &now,int pos,int w)
{
    now=cnt++;
    if(l==r)
    {
        tr[now].val=w;
        return;
    }
    tr[now].l=tr[per].l;
    tr[now].r=tr[per].r;
    int mid=l+r>>1;
    if(mid>=pos)
    {
        modify(l,mid,tr[per].l,tr[now].l,pos,w);
    }
    else
    {
        modify(mid+1,r,tr[per].r,tr[now].r,pos,w);
    }
}
int query(int l,int r,int ver,int pos)
{
    if(l==r)
    {
        return tr[ver].val;
    }
    int mid=l+r>>1;
    if(mid>=pos)
    {
        return query(l,mid,tr[ver].l,pos);
    }
    else
    {
        return query(mid+1,r,tr[ver].r,pos);
    }
}
int main()
{
    int n,q;
    scanf("%d %d",&n,&q);
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&a[i]);
    }
    build(1,n,root[0]);
    int ver,op,pos,w,id=0;
    for(int i=1;i<=q;i++)
    {
        scanf("%d %d",&ver,&op);
        if(op&1)
        {
            scanf("%d %d",&pos,&w);
            modify(1,n,root[ver],root[++id],pos,w);
        }
        else
        {
            scanf("%d",&pos);
            printf("%d\n",query(1,n,root[ver],pos));
            root[++id]=root[ver];
        }
    }

}

强调:

结构体中的l,r代表的是左右孩子节点,建树过程中了l,r 代表的就是左右孩子节点,root[i],代表的是每一个版本的根节点,通过根节点,可以找到这个版本的所有点,我们把当前版本和上一版本连起来即可

理解后打的代码:

#include<bits/stdc++.h>
using namespace std;
//可持久化数组
const int maxn=1e6+5;
struct node
{
    int l,r,val;
} tr[maxn*40];
int root[maxn];//根节点
int a[maxn];
int cnt;
void build(int l,int r,int &now)//建一颗初始版本的树
{
    now=++cnt;
    if(l==r)
    {
        tr[now].val=a[l];
        return ;
    }
    int m=l+r>>1;
    build(l,m,tr[now].l);
    build(m+1,r,tr[now].r);
}
void modify(int l,int r,int ver,int &now,int pos,int w)//修改一颗树
{
    now=++cnt;
    tr[now]=tr[ver];
    if(l==r)
    {
        tr[now].val=w;
        return ;
    }
    int m=l+r>>1;
    if(m>=pos)
    {
        modify(l,m,tr[ver].l,tr[now].l,pos,w);
    }
    else
    {
        modify(m+1,r,tr[ver].r,tr[now].r,pos,w);
    }
}
int query(int l,int r,int ver,int pos)
{
    if(l==r)
    {
        return tr[ver].val;
    }
    int m=l+r>>1;
    if(m>=pos)
    {
        return query(l,m,tr[ver].l,pos);
    }
    else
    {
        return query(m+1,r,tr[ver].r,pos);
    }
}
int main()
{
    int n,q;
    scanf("%d %d",&n,&q);
    for(int i=1; i<=n; i++)
    {
        scanf("%d",&a[i]);
    }
    build(1,n,root[0]);
    int id=0;
    while(q--)
    {
        int ver,opt,pos,w;
        scanf("%d %d",&ver,&opt);
        if(opt&1)
        {
            scanf("%d %d",&pos,&w);
            modify(1,n,root[ver],root[++id],pos,w);
        }
        else
        {
            scanf("%d",&pos);
            query(1,n,root[ver],pos);
            printf("%d\n",query(1,n,root[ver],pos));
            root[++id]=root[ver];
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值