主席树(可持久化线段树)
我真不知道这俩有啥区别…
前置思想
动态开点,前缀和,标记永久化。
思想
在修改线段树时,我们发现不同时期的线段树有大量重合,可以只改变线段树的一部分就做到一次更新整个的操作,这一部分通常只有 log n \log{n} logn 级别。
每次从根开始修改,但是不直接覆盖,而是每次都增加一个新的根,并存入一个 r o o t root root 数组,这样就能做到查询不同时期的树。
在修改时,我们直接将原本的点复制到新点上,修改要修改的部分,这样就可以做到既不影响之前,也能做到更新。
主席树模版:
struct Persistable_Segment_Tree{
#define ls(p) (tr[p].ls)
#define rs(p) (tr[p].rs)
#define mid(p) (tr[p].l+tr[p].r>>1)
int tot,n;//动态开点
int root[N];//存储不同时期的线段树的根,然后就可以查询了
struct node{
int l,r,len;//这些必要时也可以去掉
}tr[N*25];//空间一般开2^5倍,除非题目卡空间
void init(int _n){
n=_n;tot=root[0]=1;
build(root[0],1,n);
}
void build(int p,int l,int r){
tr[p]={l,r,r-l+1};
if(l==r)return;
ls(p)=++tot,rs(p)=++tot;
build(ls(p),l,mid(p));
build(rs(p),mid(p)+1,r);
}
void update(int x,int d,int p,int q){
tr[q]=tr[p];//新建节点,直接复制,这样就相当于新建一棵只改变这一条路径的线段树
if(tr[q].len==1)return;
if(x<=mid(p))ls(q)=++tot,update(x,d,ls(p),ls(q));
else rs(q)=++tot,update(x,d,rs(p),rs(q));
}//这里展示的是单点修改
int query(int p,int x){
if(tr[p].len==1)return ...;
if(x<=mid(p))return query(ls(p),x);
return query(rs(p),x);
}//单点查询
}seg;
更多操作还是看例题吧。
模版题
- 单点修改+单点查询
P3919 【模板】可持久化线段树 1(可持久化数组) - 洛谷 | 计算机科学教育新生态 (luogu.com.cn).
#include<iostream>
#include<cstring>
#include<string>
#include<cmath>
#include<cstdio>
#include<algorithm>
#include<queue>
#include<map>
#include<stack>
#include<list>
#include<set>
#include<vector>
#include<iomanip>
#include<cctype>
#include<ctime>
#include<sstream>
#define INF 0x3f3f3f3f
#define ll long long
#define rg register
#define min(a,b) ((a)>(b)?(b):(a))
#define max(a,b) ((a)<(b)?(b):(a))
#define tomax(a,b) ((a)=max((a),(b)))
#define tomin(a,b) ((a)=min((a),(b)))
#define FOR(i,a,b) for(rg int i=(a);i<=(b);++i)
#define DOR(i,a,b) for(rg int i=(a);i>=(b);--i)
#define main Main();signed main(){ios::sync_with_stdio(0);cin.tie(0);return Main();}signed Main
using namespace std;
const int N=1e6+10,M=1e6+10;
int n,m,a[N];
struct Persistable_Segment_Tree{
#define ls(p) (tr[p].ls)
#define rs(p) (tr[p].rs)
int tot,n;
int root[N];
struct node{
int l,r,len;
int ls,rs;
int sum;
}tr[N*25];
void init(int _n){
n=_n;tot=root[0]=1;
build(root[0],1,n);
}
#define mid(p) (tr[p].l+tr[p].r>>1)
void build(int p,int l,int r){
tr[p]={l,r,r-l+1,-1,-1,0};
if(l==r)return tr[p].sum=a[l],void();
ls(p)=++tot,rs(p)=++tot;
build(ls(p),l,mid(p));
build(rs(p),mid(p)+1,r);
}
void update(int x,int d,int p,int q){
tr[q]=tr[p];
if(tr[q].len==1)return tr[q].sum=d,void();
if(x<=mid(p))ls(q)=++tot,update(x,d,ls(p),ls(q));
else rs(q)=++tot,update(x,d,rs(p),rs(q));
}
int query(int p,int x){
if(tr[p].len==1)return tr[p].sum;
if(x<=mid(p))return query(ls(p),x);
return query(rs(p),x);
}
}seg;
signed main(){
cin>>n>>m;
FOR(i,1,n)cin>>a[i];
seg.init(n);
FOR(i,1,m){
int pre,opt,x;cin>>pre>>opt>>x;
if(opt==1){
int d;cin>>d;
seg.root[i]=++seg.tot;
seg.update(x,d,seg.root[pre],seg.root[i]);
}else {
seg.root[i]=seg.root[pre];
cout<<seg.query(seg.root[pre],x)<<endl;
}
}
return 0;
}
- 单点修改+区间二分(查询)
Kth number,十分经典的题目。
#include<bits/stdc++.h>
#define FOR(i,a,b) for(register int i=(a);i<=(b);++i)
#define DOR(i,a,b) for(register int i=(a);i>=(b);--i)
#define main Main();signed main(){ios::sync_with_stdio(0);cin.tie(0);return Main();}signed Main
using namespace std;
const int N=1e5+10;
int T;
int n,m,a[N];
int b[N],cnt;
struct Persistable_Segment_Tree{
#define ls(p) (tr[p].ls)
#define rs(p) (tr[p].rs)
int tot,n;
int root[N];
struct node{
int ls,rs;
int sum;
}tr[N<<5];
void init(int _n){
n=_n;tot=0;
root[0]=build(1,n);
}
#define mid (l+r>>1)
int build(int l,int r){
int p=++tot;
tr[p]={-1,-1,0};
if(l==r)return p;
ls(p)=build(l,mid),rs(p)=build(mid+1,r);
return p;
}
int update(int p,int x,int l,int r){
int q=++tot;
tr[q]=tr[p];++tr[q].sum;
if(l==r)return q;
if(x<=mid)ls(q)=update(ls(p),x,l,mid);
else rs(q)=update(rs(p),x,mid+1,r);
return q;
}
int query(int p,int q,int k,int l,int r){
if(l==r)return l;
int x=tr[ls(q)].sum-tr[ls(p)].sum;
if(x>=k)return query(ls(p),ls(q),k,l,mid);
return query(rs(p),rs(q),k-x,mid+1,r);
}
#undef mid
}seg;
void Cu_main(){
cin>>n>>m;
FOR(i,1,n)cin>>a[i],b[i]=a[i];
sort(b+1,b+n+1);
cnt=unique(b+1,b+n+1)-b-1;
seg.init(cnt);
FOR(i,1,n){
a[i]=lower_bound(b+1,b+cnt+1,a[i])-b;
seg.root[i]=++seg.tot;
seg.root[i]=seg.update(seg.root[i-1],a[i],1,cnt);
}
while(m--){
int l,r,k;cin>>l>>r>>k;
cout<<b[seg.query(seg.root[l-1],seg.root[r],k,1,cnt)]<<endl;
}
}
signed main(){
for(cin>>T;T;--T)Cu_main();
return 0;
}/*权值线段树->主席树*/
- 区间修改+区间(单点)查询
To the moon - HDU 4348 - Virtual Judge (vjudge.net).
#include<iostream>
#define ll long long
#define int long long
#define FOR(i,a,b) for(register int i=(a);i<=(b);++i)
#define DOR(i,a,b) for(register int i=(a);i>=(b);--i)
#define main Main();signed main(){ios::sync_with_stdio(0);cin.tie(0);return Main();}signed Main
using namespace std;
const int N=1e5+10;
int T,cas;
int n,m;
ll a[N];
struct Persistable_Segment_Tree{
#define ls(p) (tr[p].ls)
#define rs(p) (tr[p].rs)
int tot,n,root[N];
struct node{
int ls,rs;
ll sum,mark;
}tr[N<<4];
void init(int _n){
n=_n;tot=root[0]=0;
build(root[0],1,n);
}
#define mid (L+R>>1)
void push_up(int p,int len){
tr[p].sum=tr[ls(p)].sum+tr[rs(p)].sum+1ll*tr[ls(p)].mark*(len-len/2)+1ll*tr[rs(p)].mark*(len/2);
}
void build(int &p,int L,int R){
p=++tot;tr[p]={0,0,0,0};
if(L==R)return tr[p].sum=a[L],void();
build(ls(p),L,mid),build(rs(p),mid+1,R);
push_up(p,R-L+1);
}
void update(int &p,int q,int l,int r,int d,int L,int R){
p=++tot;tr[p]=tr[q];
if(l<=L&&R<=r)return tr[p].mark+=d,void();
if(l<=mid)update(ls(p),ls(q),l,r,d,L,mid);
if(mid<r)update(rs(p),rs(q),l,r,d,mid+1,R);
push_up(p,R-L+1);
}
ll query(int p,int l,int r,ll mark,int L,int R){
if(l<=L&&R<=r)return tr[p].sum+1ll*(tr[p].mark+mark)*(R-L+1);
ll ans=0;
if(l<=mid)ans+=query(ls(p),l,r,mark+tr[p].mark,L,mid);
if(mid<r)ans+=query(rs(p),l,r,mark+tr[p].mark,mid+1,R);
return ans;
}
#undef mid
}seg;
void Cu_main(){
if(cas)cout<<endl;cas=1;
FOR(i,1,n)cin>>a[i];
seg.init(n);T=0;
while(m--){
char opt;cin>>opt;
if(opt=='C'){
int l,r,d;cin>>l>>r>>d;++T;
seg.update(seg.root[T],seg.root[T-1],l,r,d,1,n);
}else if(opt=='Q'){
int l,r;cin>>l>>r;
cout<<seg.query(seg.root[T],l,r,0,1,n)<<endl;
}else if(opt=='H'){
int l,r,t;cin>>l>>r>>t;
cout<<seg.query(seg.root[t],l,r,0,1,n)<<endl;
}else {
cin>>T;
seg.tot=seg.root[T+1];
}
}
}
signed main(){
while(cin>>n>>m)Cu_main();
return 0;
}
相关资料
- 可持久化线段树 - OI Wiki (oi-wiki.org)
- 可持久化线段树(主席树)攻略 - 知乎 (zhihu.com).
- 算法学习笔记(50): 可持久化线段树 - 知乎 (zhihu.com).
- 数据结构学习笔记(7) 主席树 - 知乎 (zhihu.com).
- 「主席树」和「可持久化线段树」有什么区别? - 知乎 (zhihu.com).
- 良心的可持久化线段树教程 - 胡小兔 - 博客园 (cnblogs.com).

本文介绍了主席树和可持久化线段树的区别,强调了在处理动态修改和查询需求时,主席树通过保存不同时期的线段树根节点来减少重复计算,提供了一种高效的数据结构解决方案。文章还给出了相应的模板和示例应用。
4388

被折叠的 条评论
为什么被折叠?



