可持久化数组再理解

吐槽:

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

可持久化思想:

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

对于多版本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];
        }
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
1) 基于以上代码,解决收支记录不能保存的问题。理解对象的序列化和持久化,熟悉对象流的读取和写入方法,掌握数组的声明和动态内存申请。 2) 初步设计将“保存记录”和“读取记录”功能集成到成员方法里。在FamilyAccount类的main方法,原来的2个局部变量:基本金balance和收支明细details需要移至方法外,作为成员变量,方便各成员方法使用和共享。同时增加的成员变量还有:长度为100的数组用来存储收支记录;计数器用来计算目前收支记录数;文件名说明保存收支记录的位置。 3) FamilyAccount类增加“保存记录”方法,执行位置在用户确认退出系统后。思路:如果收支记录数发生变化,首先将收支记录数组依次放置到临时数组,再调用写对象流方法,将临时数组写到文件里。在实验报告详细描述此功能实现。 4) FamilyAccount类增加“读取记录”方法,执行位置在main方法菜单显示循环前。思路:如果存储收支记录的文件存在,首先调用读对象流方法,将数据读到临时数组,再依次放置到收支记录数组。在实验报告详细描述此功能实现。 5) FamilyAccount类的main方法增加“放置记录”功能,执行位置在菜单2和3的收支明细details拼串完毕后。思路:将每一次收支记录包装为一维数组,然后依次放到收支记录数组里,计数器自加1。 6) 在实验过程详细记录分析思路和操作步骤,包含文字、关键源码、图形、表格等。
最新发布
05-24

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值