zoukankan      html  css  js  c++  java
  • P3919 【模板】可持久化数组(可持久化线段树/平衡树)(入门第一题)

    学习博客:http://www.cnblogs.com/flashhu/p/8297581.html

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

    很裸的可持久化线段树板子题。可持久嘛!就是当出现历史版本的时候,能够非常方便地维护一个区间的历史版本。
    自然,我们需要建NN棵线段树。最粗暴的想法,对每个新版本都把原版本内容复制一遍,然后修改对应的值。这根本不用想,直接MLE+TLE。那维护历史版本又是怎样实现的呢?
    对于本题,每个版本的序列,我们可以建一棵线段树来维护它,所有非叶子节点表示的是一段区间,而叶子节点就表示序列的每一个值了。
    举个栗子,样例中初始版本可以长这样——

    而版本1只是查询了一下(线段树基本操作,这里不再赘述),然后跟初始版本一模一样。这就没必要复制了嘛!我们设版本ii有一个根节点rootirooti(表示整段区间),根节点有左右儿子,那么我们直接让root1root1的左右儿子指向root0root0的左右儿子就好了,根本不用复制整个线段树嘛!
    那再来看看修改操作。比如从版本1~2。1和0是一样的,而版本2会长这样——

    有没有发现1和2真的很像?其实从前到后只改变了一个节点!那么其他相同的地方,我们可不可以共用一段内存呢?

    没错,每次创建一个新的版本时,只要新建log2nlog2⁡n个节点,也就是只保存从新版本的根节点到更新的那一个叶子节点的路径就可以了,不在此路径上的左/右儿子只要接原版本对应区间的对应儿子就可以啦。我们可以保证,从对应版本的根节点一定能访问到对应叶子节点的值。

    看代码:

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    typedef long long LL;
    const LL mod=1e9+7;
    const LL INF=1e9+7;
    const int maxn=1e6+50;
    int N,M;
    int tot=0;
    int a[maxn];//输入数组
    int root[maxn<<5];//root[i]表示版本号问i的线段树的根节点编号  数组开大点
    
    struct Node
    {
        int lc,rc,v;
    }t[maxn<<5];//数组开大点
    
    int Build(int l,int r)
    {
        int pos=++tot;
        if(l==r)
        {
            t[pos].v=a[l];
            return pos;
        }
        int mid=(l+r)>>1;
        t[pos].lc=Build(l,mid);
        t[pos].rc=Build(mid+1,r);
        return pos;
    }
    int Update(int old,int tar,int c,int l,int r)//老版本 修改a[tar]为c
    {
        int pos=++tot;
        if(l==r)
        {
            t[pos].v=c;
            return pos;
        }
        t[pos].lc=t[old].lc;
        t[pos].rc=t[old].rc;//复制老树
    
        int mid=(l+r)>>1;
        if(tar<=mid) t[pos].lc=Update(t[old].lc,tar,c,l,mid);
        else t[pos].rc=Update(t[old].rc,tar,c,mid+1,r);
        return pos;
    }
    int query(int pos,int p,int l,int r)
    {
        if(l==r)
        {
            return t[pos].v;
        }
        int mid=(l+r)>>1;
        if(p<=mid) return query(t[pos].lc,p,l,mid);
        else return query(t[pos].rc,p,mid+1,r);
    }
    int main()
    {
        scanf("%d%d",&N,&M);
        for(int i=1;i<=N;i++) scanf("%d",&a[i]);
        root[0]=Build(1,N);
        int v,x,l,w;
        for(int i=1;i<=M;i++)
        {
            scanf("%d%d%d",&v,&x,&l);
            if(x==1)
            {
                scanf("%d",&w);
                root[i]=Update(root[v],l,w,1,N);
            }
            else
            {
                root[i]=root[v];//直接复制就行  因为都一样
                printf("%d
    ",query(root[v],l,1,N));
            }
        }
        return 0;
    }
    当初的梦想实现了吗,事到如今只好放弃吗~
  • 相关阅读:
    【网摘】sql 语句修改字段名称以及字段类型
    转:关于bugfree的一些不得不说的事
    excel打乱各行的顺序,实现无序随机排列
    易语言报错:无法定位链接器!请检查 toolslink.ini 中的配置是否正确。 静态连接失败
    关于a标签的onclick和href谁先执行的问题
    关于a标签的onclick和href谁先执行的问题
    关于a标签的onclick和href谁先执行的问题
    易语言-子程序的参数属性
    SQL 随机取出一条数据
    为梦想,每天坚持30分钟
  • 原文地址:https://www.cnblogs.com/caijiaming/p/10886425.html
Copyright © 2011-2022 走看看