可持久化线段树(主席树)
我们都知道线段树采用分治的逻辑将区间进行划分,支持单点修改,区间查询。
那么每修改一个叶子节点,只会修改经过它路径的节点,即 logn 个节点。
运用这个性质,我们现在想要对一个区间进行不同版本的更新,即加入时间这个维度,查询不同时间的该区间。
这时候,主席树就诞生啦!
每次单点修改,我们只需要修改 logn 个点,同时我们还需要保留执之前版本的信息。那么我们所做的不是修改,而是添加,添加 logn 个节点,其中包括这一版本的根节点。
到这里,我们明白主席树所耗用的空间是巨大的,假设有n个节点,m次修改,那么所费空间为 O(nlogn+mlogn)。
如此巨大的空间耗费之下,所储存的信息想必也是非常之多的。事实上,主席树的应用十分广泛,例如可以用来求区间第 k 大值,求区间 l,r 中介于 x,y 的值等等。
代码
#include<bits/stdc++.h> using namespace std; const int N=4e7+10; int n,m,t,top,rt,mode,x,y; int f[N],a[N],root[N]; struct kkk{ int l,r,val; }tree[N]; int clone(int node){ top++; tree[top]=tree[node]; return top; } int maketree(int node,int begin,int end){ node=++top; if(begin==end){ tree[node].val=a[begin]; return top; } int mid=begin+end>>1; tree[node].l=maketree(tree[node].l,begin,mid); tree[node].r=maketree(tree[node].r,mid+1,end); return node; } int update(int node,int begin,int end,int x,int val){ node=clone(node); if(begin==end) tree[node].val=val; else{ int mid=begin+end>>1; if(x<=mid) tree[node].l=update(tree[node].l,begin,mid,x,val); else tree[node].r=update(tree[node].r,mid+1,end,x,val); } return node; } int query(int node,int l,int r,int x){ if(l==r) return tree[node].val; else{ int mid=l+r>>1; if(x<=mid) return query(tree[node].l,l,mid,x); else return query(tree[node].r,mid+1,r,x); } } int main(){ ios::sync_with_stdio(0),cin.tie(0),cout.tie(0); cin>>n>>m; for(int i=1;i<=n;++i) cin>>a[i]; root[0]=maketree(0,1,n); for(int i=1;i<=m;++i){ cin>>rt>>mode>>x; if(mode==1){ cin>>y; root[i]=update(root[rt],1,n,x,y); } else{ cout<<query(root[rt],1,n,x)<<'\n'; root[i]=root[rt]; } } return 0; }
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 按钮权限的设计及实现