洛谷--3919可持久化线段树

题目链接https://www.luogu.org/problemnew/show/P3919

时空限制 3000ms / 512MB

题目描述
如题,你需要维护这样的一个长度为 N 的数组,支持如下几种操作

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

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

此外,每进行一次操作(对于操作2,即为生成一个完全一样的版本,不作任何改动),就会生成一个新的版本。版本编号即为当前操作的编号(从1开始编号,版本0表示初始状态数组)

在这里插入图片描述

输入输出样例

输入样例#1:

5 10
59 46 14 87 41
0 2 1
0 1 1 14
0 1 1 57
0 1 1 88
4 2 4
0 2 5
0 2 4
4 2 1
2 2 2
1 1 5 91

输出样例#1:

59
87
41
87
88
46

在这里插入图片描述
样例说明:

一共11个版本,编号从0-10,依次为:

  • 0 : 59 46 14 87 41

  • 1 : 59 46 14 87 41

  • 2 : 14 46 14 87 41

  • 3 : 57 46 14 87 41

  • 4 : 88 46 14 87 41

  • 5 : 88 46 14 87 41

  • 6 : 59 46 14 87 41

  • 7 : 59 46 14 87 41

  • 8 : 88 46 14 87 41

  • 9 : 14 46 14 87 41

  • 10 : 59 46 14 87 91


emmm,很标准的可持续化线段树。。。。
对于这玩意儿有点类似于字典树。。。将每个版本的根节点存下来,要找的时候就直接从该根节点找下来就行了:

在这里插入图片描述
看看图应该就大概理解了。。。
由于儿子是唯一的,所以我们只记录左右儿子就行了。。。每次更新的时候将前一个版本的左右儿子信息传给新版本,再根据情况覆盖左儿子或右儿子就行了。。。洛谷有一个挺大的坑的。。。对于query这一块如果这么写:

int update(int ed,int l,int r,int p,int k)
{
    int pos=++tot;
    if (l==r){
        tree[pos].val=k;
        return pos;
    }
    tree[pos].ls=tree[ed].ls;
    tree[pos].rs=tree[ed].rs;
    int mid=(l+r)>>1;
    if (mid>=p) tree[pos].ls=update(tree[ed].ls,l,mid,p,k);
    else tree[pos].rs=update(tree[ed].rs,mid+1,r,p,k);
    return pos; 
}

这就会MLE+RE。。。
在这里插入图片描述
只能改成void 然后用全局变量保存ans,然后到处补return。。。

以下是AC代码:

#include <bits/stdc++.h>
using namespace std;
const int mac=1e6+10;
struct node
{
    int ls,rs,val;
}tree[mac*20];
int a[mac],tot=0,edtor[mac]={1},ans;
int build(int l,int r)
{
    int pos=++tot;
    if (l==r){
        tree[pos].val=a[l];
        return pos;
    }
    int mid=(l+r)>>1;
    tree[pos].ls=build(l,mid);
    tree[pos].rs=build(mid+1,r);
    return pos;
}
int update(int ed,int l,int r,int p,int k)
{
    int pos=++tot;
    if (l==r){
        tree[pos].val=k;
        return pos;
    }
    tree[pos].ls=tree[ed].ls;
    tree[pos].rs=tree[ed].rs;
    int mid=(l+r)>>1;
    if (mid>=p) tree[pos].ls=update(tree[ed].ls,l,mid,p,k);
    else tree[pos].rs=update(tree[ed].rs,mid+1,r,p,k);
    return pos; 
}
void query(int ed,int l,int r,int p)
{
    if (l==r){
        ans=tree[ed].val;
        return;
    } 
    int mid=(l+r)>>1;
    if (mid>=p) query(tree[ed].ls,l,mid,p);
    else query(tree[ed].rs,mid+1,r,p);
    return;
}
int main()
{
    //freopen("test.in","r",stdin);
    int n,m;
    scanf ("%d%d",&n,&m);;
    for (int i=1; i<=n; i++){
        scanf ("%d",&a[i]);
    }
    build (1,n);
    int v,id,loc,val;
    for (int i=1; i<=m; i++){
        scanf ("%d%d%d",&v,&id,&loc);
        if (id==1){
            scanf ("%d",&val);
            edtor[i]=update(edtor[v],1,n,loc,val);
        }
        else {
            edtor[i]=edtor[v];
            query(edtor[v],1,n,loc);
            printf ("%d\n",ans);
        }
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值