可持久化数据结构

简介: 可持久化数据结构

这节课学的不咋会,而且感觉不咋通用,就随便写写看吧


适用的是数发生变化和求区间的某种属性等等


可持久化的前提:本身的拓扑排序结构不变


可持久化的操作相当于git仓库的存储方式,可以查找和存储所有的历史版本,核心思想只记录每一个版本与上一个版本不同的节点 ,然后用一棵树的结构来连接不同的地方,相同的地方直接copy过来就行

1.tire树的可持久化

(1)普通的tire ——》版本一

(2) 版本一 ——》版本二

(3)版本二 ——》版本三

(4)版本三 ——》版本四


最大异或

256. 最大异或和 - AcWing题库

#include<bits/stdc++.h>
using namespace std;
const int N=600010,M=N*25;
int n,m;
int s[N];//存异或前缀和
int tr[M][2],max_id[M];//tr存tire树版本,max_id存的是某个节点最大的id
int root[N],idx;//记录根节点序列
void insert(int i,int k,int p,int q)//当前第几位 当前处理到第几位 上一个版本 当前最新版本
{
    if(k<0)
    {
        max_id[q]=i;//处理到最后一位,则max_id=i
        return;
    }
    int v=s[i]>>k&1;//获取当前位
    if(p) tr[q][v^1]=tr[p][v^1];//假如上一个节点存在,直接复制过来
    tr[q][v]=++idx;//给当前位开一个新的点
    insert(i,k-1,tr[p][v],tr[q][v]);//插入下一位
    max_id[q]=max(max_id[tr[q][0]],max_id[tr[q][1]]);//左右子节点取最大
}
int query(int root,int C,int L)//当前节点 C就是s[n]^x L就是左边限制
{
    int  p=root;
    for(int i=23;i>=0;i--)
    {
        int v=C>>i&1;//获取当前位
        if(max_id[tr[p][v^1]]>=L) p=tr[p][v^1];//假如这个节点的当前位的对立面有的化
        else p=tr[p][v];//反之只能将就了
    }
    return C^s[max_id[p]];//返回答案
}
int main()
{
    scanf("%d%d",&n,&m);
    max_id[0]=-1;//0初始化为最小的数就行了
    root[0]=++idx;//第0个版本分配一个idx
    insert(0,23,0,root[0]);//处理第0个版本
    for(int i=1;i<=n;i++)
    {
        int x;
        scanf("%d",&x);
        s[i]=s[i-1]^x;//前缀和异或
        root[i]=++idx;//开辟新的节点
        insert(i,23,root[i-1],root[i]);//插入这个版本
    }
    char op[2];
    int l,r,x;
    while(m--)
    {
        scanf("%s",op);
        if(*op=='A')
        {
            scanf("%d",&x);
            n++;
            s[n]=s[n-1]^x;//前缀异或和
            root[n]=++idx;//开辟新的节点
            insert(n,23,root[n-1],root[n]);//更新新的版本
        }
        else
        {
            scanf("%d%d%d",&l,&r,&x);
            printf("%d\n",query(root[r-1],s[n]^x,l-1));//输出r-1版本中数大于l-1的数
        }
    }
    return 0;
}

2.线段树的可持久化——主席树


第K小数

255. 第K小数 - AcWing题库




先求1~R的版本中[l,r]的个数cnt1,在求1~L的版本中[l,r]的个数cnt2,cnt=cnt1-cnt2则为下标区间[L,R]在这个数值[l,r]的增加的个数,然后二分求第k小数,k<=cnt递归左边反之递归右边

#include<bits/stdc++.h>
using namespace std;
const int N=100010,M=10010;
int n,m;
int a[N];
vector<int> nums;
struct Node
{
    int l,r;//这里的l,r不是区间是左右子树的下标
    int cnt;//记录当前树的这个区间有多少个数
}tr[N*4+N*17];
int root[N],idx;//记录不同的版本
int find(int x)//离散化的二分查找
{
    return lower_bound(nums.begin(),nums.end(),x)-nums.begin();
}
int built(int l,int r)//建立线段树
{
    int p=++idx;//分配一个节点
    if(l==r) return p;
    int mid=l+r>>1;
    tr[p].l=built(l,mid),tr[p].r=built(mid+1,r);//左右建立线段树
    return p;
}
int insert(int p,int l,int r,int x)
{
    int q=++idx;//开辟一个新的节点,也即新的版本
    tr[q]=tr[p];//先把上一个版本复制过来
    if(l==r)//假如是叶节点
    {
        tr[q].cnt++;
        return q;
    }
    int mid=l+r>>1;
    if(x<=mid) tr[q].l=insert(tr[p].l,l,mid,x);//假如在左边,则插入在左边
    else tr[q].r=insert(tr[p].r,mid+1,r,x);//假如在右边,则插入在右边
    tr[q].cnt=tr[tr[q].l].cnt+tr[tr[q].r].cnt;//更新一下cnt
    return q;
}
int query(int q,int p,int l,int r,int k)
{
    if(l==r) return l;//假如是叶节点,则答案就是叶节点
    int cnt=tr[tr[q].l].cnt-tr[tr[p].l].cnt;//算一下左边区间增加的cnt个数
    int mid=l+r>>1;
    if(k<=cnt) return query(tr[q].l,tr[p].l,l,mid,k);//假如左边的数大于等于k,则第k小数在左边
    else return query(tr[q].r,tr[p].r,mid+1,r,k-cnt);//反之在右边,右边则找第k-cnt小数,因为左边已经有cnt个小数了
}
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&a[i]);
        nums.push_back(a[i]);//进行离散化
    }
    //去重
    sort(nums.begin(),nums.end());
    nums.erase(unique(nums.begin(),nums.end()),nums.end());
    root[0]=built(0,nums.size()-1);//初始化第0个版本,也即创立线段树
    for(int i=1;i<=n;i++)
        root[i]=insert(root[i-1],0,nums.size()-1,find(a[i]));//把每个值插进当前版本里
    while(m--)
    {
        int l,r,k;
        scanf("%d%d%d",&l,&r,&k);
        printf("%d\n",nums[query(root[r],root[l-1],0,nums.size()-1,k)]);//询问整个区间的l-1~r版本的增加的数中的第k小数
    }
    return 0;
}
相关文章
|
1月前
|
存储 NoSQL 索引
【数据结构】数据结构学什么?
【数据结构】数据结构学什么?
37 5
|
3月前
|
消息中间件 缓存 调度
常见的八种数据结构
常见的数据结构包括数组、链表、队列、栈、树、堆、哈希表和图,每种数据结构都有其特点
61 3
|
4月前
|
存储 算法 索引
|
6月前
|
存储 算法 C#
C#编程与数据结构的结合
【4月更文挑战第21天】本文探讨了C#如何结合数据结构以构建高效软件,强调数据结构在C#中的重要性。C#作为面向对象的编程语言,提供内置数据结构如List、Array和Dictionary,同时也支持自定义数据结构。文章列举了C#实现数组、链表、栈、队列等基础数据结构的示例,并讨论了它们在排序、图算法和数据库访问等场景的应用。掌握C#数据结构有助于编写高性能、可维护的代码。
56 3
|
6月前
|
存储 算法
【数据结构】什么是数据结构?
【数据结构】什么是数据结构?
54 0
|
存储 容器
|
6月前
|
NoSQL 容器 消息中间件
数据结构 2.3.7
数据结构 2.3.7
|
6月前
|
算法
数据结构22
数据结构22
24 0
|
存储 算法 搜索推荐
【BaseArray 数据结构】
【BaseArray 数据结构】
|
存储 算法
【数据结构】初识(下)
【数据结构】初识(下)
73 0