• 主席树(历史版本)学习笔记


    题目背景

    标题即题意

    有了可持久化数组,便可以实现很多衍生的可持久化功能(例如:可持久化并查集)

    题目描述

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

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

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

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

    输入输出格式

    输入格式:

    输入的第一行包含两个正整数 N, MN,M, 分别表示数组的长度和操作的个数。

    第二行包含NN个整数,依次为初始状态下数组各位的值(依次为 a_iai1 leq i leq N1iN)。

    接下来MM行每行包含3或4个整数,代表两种操作之一(ii为基于的历史版本号):

    1. 对于操作1,格式为v_i 1 {loc}_i {value}_ivi 1 loci valuei,即为在版本v_ivi的基础上,将 a_{{loc}_i}aloci 修改为 {value}_ivaluei

    2. 对于操作2,格式为v_i 2 {loc}_ivi 2 loci,即访问版本v_ivi中的 a_{{loc}_i}aloci的值,生成一样版本的对象应为vi

    输出格式:

    输出包含若干行,依次为每个操作2的结果。

    输入输出样例

    输入样例#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


    (妥妥的权值线段树)

    最终图只是最后一棵线段树的样子,之前的各个线段树依旧在保存着

    直接暴力每一个状态,然后记录,超时空!

    每一次只修改一个点,不是吗?

    于是,我们只要修改这一个点,然后把这条链记录下来,不就可以了?233

    这不是熟悉的主席树吗?

    是的!

    建一棵空树,然后以原数组插入,之后对每一次修改进行一次加链操作

    然后查询就可以了。

    直接代码:

    #include<bits/stdc++.h>
    using namespace std;
    const int maxn=20000001;
    int n,m,cnt;
    int a[maxn];
    int dis[maxn];//有些不同的就是,我们要记录下来每一次改的值
    int rs[maxn];
    int ls[maxn];
    int rt[maxn];
    int build(int l,int r)
    {
        int root=++cnt;
        if(l==r)
        {
            dis[root]=a[l];//像极了线段树233
            return root;//还是要记录根的
        }
        int mid=l+r>>1;
        ls[root]=build(l,mid);
        rs[root]=build(mid+1,r);
        return root;
    }
    int updata(int l,int r,int root,int x,int k)
    {
        int newroot=++cnt;
        if(l==r)
        {
            dis[newroot]=x;//如果到叶子了就记录一下新的值
            return newroot;//还是要返回根的
        }
        ls[newroot]=ls[root];
        rs[newroot]=rs[root];
        int mid=l+r>>1;
        if(k<=mid)ls[newroot]=updata(l,mid,ls[newroot],x,k);
        else rs[newroot]=updata(mid+1,r,rs[newroot],x,k);
        return newroot;
    }
    void query(int l,int r,int root,int x)
    {
        if(l==r)
        {
            printf("%d
    ",dis[root]);//直接输出,不用差分
            return ;
        }
        int mid=l+r>>1;
        if(x<=mid)query(l,mid,ls[root],x);//直接跑,不用差分了
        else query(mid+1,r,rs[root],x);//直接跑,不用差分了
    }
    
    int main()
    {
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&a[i]);//这次没有这么多神仙操作
        }
        rt[0]=build(1,n);//建树
        for(int i=1;i<=m;i++)
        {
            int x,y,z;
            scanf("%d%d%d",&x,&y,&z);
            if(y==1)
            {
                int flag;
                scanf("%d",&flag);
                rt[i]=updata(1,n,rt[x],flag,z);//同kth,加链,记录根节点
            }
            if(y==2)
            {
                rt[i]=rt[x];
                query(1,n,rt[x],z);//查询
            }
        }
        return 0;
    }

    (完)

  • 相关阅读:
    .NET的DTO映射工具AutoMapper
    使用TeamCity对项目进行可持续集成管理
    SpecFlow
    重构--改善既有代码的设计
    EntityFramework 7 开发纪录
    Solr索引
    DDD分层架构之值对象(层超类型篇)
    C#异步Socket示例
    Cnblogs API
    白鸦三次创业反思:公司遇问题 怎么走都对(转)
  • 原文地址:https://www.cnblogs.com/ajmddzp/p/11055963.html
Copyright © 2020-2023  润新知