可持久化数据结构QwQ(持续更新中)

可持久化留下的迹象 我们俯身欣赏

          ——《膜你抄》By Menci&24OI

Micardi最近在学可持久化的东西,可持久化线段树、可持久化并查集、可持久化01Trie......等等等等恶心人的东西QwQ。不过呢,犯其至难,图其至远,如果能静下心来搞定这些乱七八糟的玩意,相信对自己的OI之路也会大有裨益吧QwQ。

 

言归正传!


1.主席树(可持久化线段树)

属于Micardi的数据结构!

什么是主席树?

主席树,本名可持久化线段树——顾名思义,它是一种基于线段树、并且可持久的一种数据结构。为什么要叫主席树?因为它是由一位名叫黄嘉泰的巨佬发明的......而这位巨佬的名字缩写对应了前国家主席2333333因此得名。主席树还有个名字叫函数式线段树,这我们就不用去管因为什么了因为我也不太懂啊QAQ......

主席树怎么用?

来,让我们步入正题:

Q1:给出序列an,你需要对该序列进行两种操作:1.在某一历史版本基础上修改某个元素值;2.查询某一历史版本里的某一元素值。我们定义初始序列为版本0,每进行一次操作都会生成一个新的版本。序列内元素个数n、操作数m均≤5×106

单点修改+单点查询,很自然地,我们想到了线段树、树状数组(分块、莫队、splay)......于是最暴力的做法显而易见:每一个版本我们都建立一颗线段树,然后模拟修改和查询。时间复杂度O(mnlogn),显然无法承受——而且m棵线段树,真的不怕赤果果的MLE嘛?

这个时候,我们就要搬出我们的主席树啦!

我们考虑这样一个问题:从算法正确性上考虑,不停地建立线段树——是对的,我们需要做的是优化这种做法可怕的时空复杂度。如何优化呢?我们要从线段树特点上下手啦:

假设已知序列1,2,3,4,5,我们建一颗线段树:

 如果我们现在要修改3,会有哪些节点被修改呢?

很显然啦,只有这个叶子节点到树根一条路上的节点会被修改——深入思考一下不难发现,对于每一次单点修改,我们其实只需要修改它->它爸->它爷爷->......->树根这些节点。由于线段树是一颗平衡二叉树,树高差不多就是logn(这么不严谨应该拖出去打死)所以我们每次只需要修改logn个节点即可——完全用不着再重新建一整颗线段树。

可是我们怎么知道要改哪些呢?——画画图也不难发现,由于线段树每一个节点左儿子的sum存储的是该节点对应区间左半边的信息,右儿子存储的是右半边的信息,假设我们要修改的位置是x,那么如果x在左半边(即x<=mid)我们就去修改左子树,保留右子树,反之亦然。这样从根节点递归到叶子结点,我们就可以完成一次修改啦:

Luogu P3919 【模板】可持久化数组(主席树):

#include<bits/stdc++.h>
#define mid ((l+r)/2)
using namespace std;

const int N=400017;

int sum[N<<5],L[N<<5],R[N<<5],rt[N<<5],a[N],cnt;
int n,m;

int build(int l,int r)
{
    int rt=++cnt;
    if(l==r)
    {
        sum[rt]=a[l];
        return rt;
    }
    L[rt]=build(l,mid);R[rt]=build(mid+1,r);
    return rt;
}

int upd(int pre,int l,int r,int x,int v)//把第pre个版本修改成v
{
    int rt=++cnt;
    L[rt]=L[pre],R[rt]=R[pre],sum[rt]=sum[pre];
    if(l==r){sum[rt]=v;return rt;}
    if(x<=mid)L[rt]=upd(L[pre],l,mid,x,v);
    else R[rt]=upd(R[pre],mid+1,r,x,v);
    return rt;
} 

int query(int pre,int l,int r,int x)
{
    if(l==r)return sum[pre];
    if(x<=mid)return query(L[pre],l,mid,x);
    else return query(R[pre],mid+1,r,x);
}

int main()
{
    scanf("%d%d",&n,&m);
    for(register int i=1;i<=n;++i)scanf("%d",&a[i]);
    rt[0]=build(1,n);
    for(register int i=1;i<=m;++i)
    {
        int opt,x,y;
        scanf("%d%d%d",&x,&opt,&y);
        if(opt==1)
        {
            int z;scanf("%d",&z);
            rt[i]=upd(rt[x],1,n,y,z);
        }
        else 
        {
            rt[i]=rt[x];
            printf("%d\n",query(rt[x],1,n,y));
        }
    }
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/Micardi/p/10233207.html
今日推荐