最近一直在搞数据结构,主席树,理解也不算特别深刻,还没有对动态开点和可持久化思想理解特别深刻,所以,今天我就准备写一篇可持久化数组来通一通脑子
新手入门题目:洛谷:可持久化数组
就题论题:
给一个数组
a[ ]:
我们可以对其进行修改,然后询问不同版本的值,最初的版本为:0,修改一次就产生一个新版本
朴素做法:
写一个二维数组
ver[i][j]
i
代表版本,然后寻问 第i
个版本第pos
个位置的值,这样对于小数据来说还行,不会MLE
,也不会TLE
,但是对于大数据来说,每次修改都是O(n)
,修改m
次,时间复杂度为O(nm)
,而且大数据会MLE,ver[10^6][10^4]
毫无疑问数组存不下,那么现在可持久化数组出现了,可持久化数组的实现利用了线段树+可持久化思想
首先:
我们建出最初版本的线段树来存版本0的数组,我们以数组大小为5为例:
根据上图,已经把图建了出来了,也不怎么耗费空间了,我们要查某一个版本的数组的值,只需要,知道某一版本的根节点即可
AC代码:
#include<bits/stdc++.h>
using namespace std;
//可持久化数组
const int maxn=1e6+5;
int root[maxn];
struct node
{
int l,r,val;
}tr[maxn*40];
int a[maxn];
int cnt;
void build(int l,int r,int &now)
{
now=++cnt;
if(l==r)
{
tr[now].val=a[l];
return;
}
int mid=l+r>>1;
build(l,mid,tr[now].l);
build(mid+1,r,tr[now].r);
}
void modify(int l,int r,int per,int &now,int pos,int w)
{
now=cnt++;
if(l==r)
{
tr[now].val=w;
return;
}
tr[now].l=tr[per].l;
tr[now].r=tr[per].r;
int mid=l+r>>1;
if(mid>=pos)
{
modify(l,mid,tr[per].l,tr[now].l,pos,w);
}
else
{
modify(mid+1,r,tr[per].r,tr[now].r,pos,w);
}
}
int query(int l,int r,int ver,int pos)
{
if(l==r)
{
return tr[ver].val;
}
int mid=l+r>>1;
if(mid>=pos)
{
return query(l,mid,tr[ver].l,pos);
}
else
{
return query(mid+1,r,tr[ver].r,pos);
}
}
int main()
{
int n,q;
scanf("%d %d",&n,&q);
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
}
build(1,n,root[0]);
int ver,op,pos,w,id=0;
for(int i=1;i<=q;i++)
{
scanf("%d %d",&ver,&op);
if(op&1)
{
scanf("%d %d",&pos,&w);
modify(1,n,root[ver],root[++id],pos,w);
}
else
{
scanf("%d",&pos);
printf("%d\n",query(1,n,root[ver],pos));
root[++id]=root[ver];
}
}
}
强调:
结构体中的l,r代表的是左右孩子节点,建树过程中了l,r 代表的就是左右孩子节点,root[i],代表的是每一个版本的根节点,通过根节点,可以找到这个版本的所有点,我们把当前版本和上一版本连起来即可
理解后打的代码:
#include<bits/stdc++.h>
using namespace std;
//可持久化数组
const int maxn=1e6+5;
struct node
{
int l,r,val;
} tr[maxn*40];
int root[maxn];//根节点
int a[maxn];
int cnt;
void build(int l,int r,int &now)//建一颗初始版本的树
{
now=++cnt;
if(l==r)
{
tr[now].val=a[l];
return ;
}
int m=l+r>>1;
build(l,m,tr[now].l);
build(m+1,r,tr[now].r);
}
void modify(int l,int r,int ver,int &now,int pos,int w)//修改一颗树
{
now=++cnt;
tr[now]=tr[ver];
if(l==r)
{
tr[now].val=w;
return ;
}
int m=l+r>>1;
if(m>=pos)
{
modify(l,m,tr[ver].l,tr[now].l,pos,w);
}
else
{
modify(m+1,r,tr[ver].r,tr[now].r,pos,w);
}
}
int query(int l,int r,int ver,int pos)
{
if(l==r)
{
return tr[ver].val;
}
int m=l+r>>1;
if(m>=pos)
{
return query(l,m,tr[ver].l,pos);
}
else
{
return query(m+1,r,tr[ver].r,pos);
}
}
int main()
{
int n,q;
scanf("%d %d",&n,&q);
for(int i=1; i<=n; i++)
{
scanf("%d",&a[i]);
}
build(1,n,root[0]);
int id=0;
while(q--)
{
int ver,opt,pos,w;
scanf("%d %d",&ver,&opt);
if(opt&1)
{
scanf("%d %d",&pos,&w);
modify(1,n,root[ver],root[++id],pos,w);
}
else
{
scanf("%d",&pos);
query(1,n,root[ver],pos);
printf("%d\n",query(1,n,root[ver],pos));
root[++id]=root[ver];
}
}
}