可持久化数组

Posted yodel

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了可持久化数组相关的知识,希望对你有一定的参考价值。

题目背景

UPDATE : 最后一个点时间空间已经放大

标题即题意

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

题目描述

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

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

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

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

输入输出格式

输入格式:

 

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

第二行包含NN个整数,依次为初始状态下数组各位的值(依次为 a_iai?1 \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??的值

 

输出格式:

 

输出包含若干行,依次为每个操作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

说明

数据规模:

对于30%的数据:1 \leq N, M \leq {10}^31N,M103

对于50%的数据:1 \leq N, M \leq {10}^41N,M104

对于70%的数据:1 \leq N, M \leq {10}^51N,M105

对于100%的数据:1 \leq N, M \leq {10}^6, 1 \leq {loc}_i \leq N, 0 \leq v_i < i, -{10}^9 \leq a_i, {value}_i \leq {10}^91N,M106,1loci?N,0vi?<i,109ai?,valuei?109

 

代码

思路1:树上dfs离线操作

#include<cstdio>
#define f(c)   for(int i=1;i<=c;i++)
using namespace std;
const int maxn = 1000009;
int head[maxn],n,m,cnt,a[maxn],ans[maxn];
struct edg{int next,to;}e[maxn];
struct node{int ver,opt,pos,val;}q[maxn]; 
bool uuz[maxn];
int I(){int x=0,f=1;char ch=getchar();
    for(;ch<0||ch>9;ch=getchar())if(ch==-)f=-1;
    for(;0<=ch&&ch<=9;ch=getchar())x=(x<<3)+(x<<1)+(ch^48);return x*f;}
void add(int x,int y){e[++cnt].next=head[x];head[x]=cnt;e[cnt].to=y;}
void dfs(int x){int last;
    q[x].opt==1?last=a[q[x].pos],a[q[x].pos]=q[x].val:ans[x]=a[q[x].pos];
    for(int i=head[x];i;i=e[i].next)dfs(e[i].to);
    if(q[x].opt==1)a[q[x].pos]=last;}
int main(){n=I();m=I();f(n)a[i]=I();
    f(m){q[i].ver=I();q[i].opt=I();q[i].pos=I();
        q[i].opt==1?q[i].val=I():uuz[i]=1;add(q[i].ver,i);}
    dfs(0);f(m)if(uuz[i])printf("%d\n",ans[i]);return 0;}

思路2:可持久化线段树

#include<cstdio>
#define f(a)  for(int i=1;i<=a;i++)
const int N=1000005;
using namespace std;
int a[N],n,q,rt[N*20];
int I(){int x=0,f=1;char ch=getchar();
    for(;ch<0||ch>9;ch=getchar())if(ch==-)f=-1;
    for(;0<=ch&&ch<=9;ch=getchar())x=(x<<3)+(x<<1)+(ch^48);return x*f;}
struct Persistable_Segment_Tree{
    int lc[N*20],rc[N*20],val[N*20],cnt;
    inline void build(int &o,int l,int r){
        o=++cnt;if(l==r){val[o]=a[l];return;}
        int mid=(l+r)>>1;build(lc[o],l,mid);build(rc[o],mid+1,r);}
    inline void ins(int &o,int pre,int l,int r,int q,int v){
        o=++cnt;lc[o]=lc[pre];rc[o]=rc[pre];val[o]=val[pre];
        if(l==r){val[o]=v;return;}int mid=(l+r)>>1;
        q<=mid?ins(lc[o],lc[pre],l,mid,q,v):ins(rc[o],rc[pre],mid+1,r,q,v);}
    inline int query(int o,int l,int r,int q){
        if(l==r)return val[o];int mid=(l+r)>>1;
        if(q<=mid)return query(lc[o],l,mid,q);
        else return query(rc[o],mid+1,r,q);}
}T;
int main(){n=I();int m=I();f(n)a[i]=I();T.build(rt[0],1,n);
    f(m){int pre=I(),opt=I(),x=I();
        if(opt==1){int v=I();T.ins(rt[i],rt[pre],1,n,x,v);}
       else printf("%d\n",T.query(rt[pre],1,n,x)),rt[i]=rt[pre];}
}

 思路3:主席树

#include<cstdio>
using namespace std;
#define MAX 1000100
#define f(c)   for(int i=1;i<=c;i++)
int I(){int x=0,f=1;char ch=getchar();
    for(;ch<0||ch>9;ch=getchar())if(ch==-)f=-1;
    for(;0<=ch&&ch<=9;ch=getchar())x=(x<<3)+(x<<1)+(ch^48);return x*f;}
int root[MAX],a[MAX],N,M;
struct Node{int l,r,ls,rs,val;}t[MAX*20];
int sum=0,tot=1;
void Build(int now,int l,int r){t[now].l=l;t[now].r=r;
    if(l==r){t[now].val=a[l];return;}
    t[now].ls=++tot;int mid=(l+r)>>1;
    Build(tot,l,mid);t[now].rs=++tot;Build(tot,mid+1,r);}
void AddNode(int now,int New,int k,int w){t[New]=t[now];
    if(t[now].l==t[now].r){t[New].val=w;return;}
    int mid=(t[now].l+t[now].r)>>1;
    if(k<=mid)t[New].ls=++tot,AddNode(t[now].ls,tot,k,w);
    else t[New].rs=++tot,AddNode(t[now].rs,tot,k,w);}
void Query(int now,int k){
    if(t[now].l==t[now].r){printf("%d\n",t[now].val);return;}
    int mid=(t[now].l+t[now].r)>>1;
    k<=mid?Query(t[now].ls,k):Query(t[now].rs,k);}
int main(){N=I();M=I();f(N)a[i]=I();
    Build(1,1,N);root[0]=1;
    f(M){int v=I(),opt=I();
        if(opt==1){int vv=I(),ww=I();AddNode(root[v],root[++sum]=++tot,vv,ww);}
        else{int vv=I();Query(root[v],vv);root[++sum]=root[v];}}return 0;}

 

以上是关于可持久化数组的主要内容,如果未能解决你的问题,请参考以下文章

持久片段和查看器

ZOJ2112 Dynamic Rank(可持久化线段树套树状数组)

ZOJ2112 Dynamic Rank(可持久化线段树套树状数组)

精心收集的 48 个 JavaScript 代码片段,仅需 30 秒就可理解!(转载)

P3919 模板可持久化数组(可持久化线段树/平衡树)

P3919 模板可持久化数组(可持久化线段树/平衡树)